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