Codeforces Round #492 (Div. 2) D. Suit and Tie

链接

http://codeforces.com/contest/996/problem/D

题目大意

给你一个序列,长度为 2 n ,其中 1 n n 种数字每个都出现了恰好两次,问你经过最少多少次交换能使得相同的数字都相邻。

题解

举个例子,为了让两个 a 相邻,我只会把左边的 a 往右交换或者把右边的 a 往左交换,它们只会往互相靠近的地方移动,否则只会徒增花费。
由于二者之间距离一定,在不考虑其它数字的情况下,交换次数就等于二者的距离。
考虑其它数字的交换对当前数字由什么影响,比如 b c c a d d a b ,如果我只想让两个 b 相邻,我完全可以把左边的一路交换过去,然后我再对 a 操作的时候,发现刚才的操作 a 没有影响。
那如果这样呢 . . . . a . . . b . . b . . . a ,同样的,这样也没有影响
那如果这样 . . . . b . . . a . . . b . . . a ,如果我把左边的 b 移过去,会使得移动 a 的费用增加 1 ,所以更优的方法是把右边的 b 移动到左边,这样会使得移动 a 的费用减少 1 b 的费用不变
从上述例子可以发现,两次相交且互不包含移动如果是相向的,会让答案增加 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;
}

猜你喜欢

转载自blog.csdn.net/fsahfgsadhsakndas/article/details/80963446