D. Suit and Tie cf996

这道题让我想起了以前一道变态题 round#324 的E题,ANTON AND LRA

这道题比较变态的地方是让你输出变化的规则。

但是仔细一想发现如果只挪每个需要向后移动的元素,就可以每次在不多移动的情况下将小于等于pos位置的元素移动到pos前面

因为每个元素需要移动的距离是一定的,在只需要保证每次使需要向前移动的元素尽量靠近末位置,需要向后的元素最终会移动到自己的位置上

这样每次从后面到前面确定一个位置,又使前面的部分达到了最优状态。

一直不断向前推进,直到第一位被确定下来。

然后就是这道题,一开始想到的贪心策略是相邻两个数字如果不同的话就找到那个距离第一个近,就交换哪个。

这样每确定两个数字都能保证确定下来的数字最小,最后自然得到最优解。

#include<bits/stdc++.h>
using namespace std;
int a[110];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=2*n;i++)
       scanf("%d",&a[i]);
    long long res=0;
    for(int i=1;i<=2*n;i+=2)//切忌低级错误!!! 
    {
        int j=i+1;
        if(a[j]==a[i])
          continue;
        int j1=i+2;
        int step1=1;
        while(a[j1]!=a[i]) j1++,step1++;
        int j2=i+2;
        int step2=2;
        while(a[j2]!=a[i+1]) j2++,step2++;
        if(step1<=step2)
        {
            for(int k=j1;k>i+1;k--)
               swap(a[k],a[k-1]),res++;
        }
        else
        {
            swap(a[i],a[i+1]);
            res++;
            for(int k=j2;k>i+1;k--)
               swap(a[k],a[k-1]),res++;
        }
    }
    printf("%lld\n",res);
}

但是还不够简单,最简单的方法是,找到和第一个数字相同的元素,直接一个个交换过去

为什么正确呢

因为每次都考虑如果需要移动的数字不在两个数字中间,则不影响结果。

如果在两个数字中间,就使得结果减一

而每次向前移动,一定会使每对和他相交的数字结果减一

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= 2 * n; i++) {
        scanf("%d", &a[i]);
        if(!l[a[i]]) l[a[i]] = i;
        else r[a[i]] = i;
    }

    for(int i = 1; i <= n; i++) {
        ans += r[i] - l[i] - 1;
    }

    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            if(l[i] < l[j] && l[j] < r[i] && r[i] < r[j]) ans--;
            if(l[j] < l[i] && l[i] < r[j] && r[j] < r[i]) ans--;
        }
    }
    printf("%d\n", ans);
    return 0;
}

这种移动数字的贪心很难找到证明方法,搞懂他们的证明很费劲

所以以后碰到这种题目,看懂贪心策略后尽量先记下来,为以后碰到类似题目找灵感,最后需要知道证明方法时再想着去证明

上一篇:第1章 Python数据模型


下一篇:微信小程序实现banner图轮播(动态获取数据),自动获取图片高度