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

链接

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

题目大意

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

题解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值