链接
http://codeforces.com/contest/996/problem/D
题目大意
给你一个序列,长度为 ,其中 到 这 种数字每个都出现了恰好两次,问你经过最少多少次交换能使得相同的数字都相邻。
题解
举个例子,为了让两个
相邻,我只会把左边的
往右交换或者把右边的
往左交换,它们只会往互相靠近的地方移动,否则只会徒增花费。
由于二者之间距离一定,在不考虑其它数字的情况下,交换次数就等于二者的距离。
考虑其它数字的交换对当前数字由什么影响,比如
,如果我只想让两个
相邻,我完全可以把左边的一路交换过去,然后我再对
操作的时候,发现刚才的操作
没有影响。
那如果这样呢
,同样的,这样也没有影响
那如果这样
,如果我把左边的
移过去,会使得移动
的费用增加
,所以更优的方法是把右边的
移动到左边,这样会使得移动
的费用减少
,
的费用不变
从上述例子可以发现,两次相交且互不包含移动如果是相向的,会让答案增加
,同向会使答案减少
。
因此我最后的算法就是对于所有的数字,都把右边的移到左边。
正确性
上述算法总有点猜想的意味,如何知道它是正确的呢?
对于每一组数字,我一定要通过一次次交换使得它们之间的距离每次减少
直至相遇。
对于成相交而互不包含分布的两组数字,一种数字的相遇一定会对另一种造成
的影响(必定存在一种),而其他情况下,两种数字不会互相影响。
我的算法保证这个影响是
,也就是说我求出的而答案一定是最优的。
代码
#include <cstdio>
#include <algorithm>
#define maxn 210
using namespace std;
int n, a[maxn], l[maxn], r[maxn];
int main()
{
int i, j, ans=0;
scanf("%d",&n);
for(i=1;i<=n<<1;i++)scanf("%d",a+i);
for(i=1;i<=n<<1;i++)
{
if(!l[a[i]])l[a[i]]=i+1;
else r[a[i]]=i;
}
for(i=1;i<=n;i++)ans+=r[i]-l[i];
for(i=1;i<=n;i++)for(j=i+1;j<=n;j++)
{
if(l[i]<=l[j] and l[j]<=r[i] and r[i]<=r[j]
or l[j]<=l[i] and l[i]<=r[j] and r[j]<=r[i])ans--;
}
printf("%d",ans);
return 0;
}