【NOIP2005】篝火晚会

题目链接:https://www.luogu.org/problemnew/show/P1053

题目巨坑!!

首先要注意bi不是连续的。

然后不难发现最少的总代价就是目标环和初始环不匹配的个数

之后30分的做法就是枚举每一个断点,一一判断不匹配的个数。

100分做法:

联想手链:如果有一些珠子,它们在第一次数珠子的时候离目标位还有x个珠子,那么下一次离目标就会还有x-1格。那么它们要么同时对上,要么同时对不上。所以我们只需随便拆一下,然后统计相同的珠子之间距离出现次数的最大值,用n减去即得到答案。

还有几个比较麻烦的地方:

建环

统计距离时的转化(取模或者特判都可以)

code:

#include
using namespace std;

const int N=5e4+10, INF=0x3f3f3f3f;
int num, ans, an[N], bs[N], s1[N], s2[N], n[N], s[N], cnta[3*N], cntb[3*N];

int main() {
    cin >> num;
    for (int i=1; i<=num; i++)
        cin >> s1[i] >> s2[i];
    for (int i=1; i<=num; i++) {
        if ((s1[s1[i]]!=i&&s2[s1[i]]!=i) || (s1[s2[i]]!=i&&s2[s2[i]]!=i)) {
            cout << "-1";
            return 0;
        }
    }
//按照顺时针和逆时针分别建环
    n[1]=s1[1], s[1]=s2[1];
    int ntmp=n[1], nlast=1, slast=1, stmp=s[1];
    for (int i=2; i<=num; i++) {
        n[ntmp]=(nlast==s1[ntmp])?s2[ntmp]:s1[ntmp];
        s[stmp]=(slast==s1[stmp])?s2[stmp]:s1[stmp];
        nlast=ntmp;
        slast=stmp;
        ntmp=n[ntmp];
        stmp=s[stmp];
    }
    an[1]=1, bs[1]=1;
    for (int i=2; i<=num; i++) {
        an[i]=n[an[i-1]];
        bs[i]=s[bs[i-1]];
    }
	int tmp=-INF, tmpa, tmpb;
//统计,这里是特判的
    for (int i=1; i<=num; i++) {
    	if (an[i]-i<0) {
    		cnta[an[i]-i+num]++;
    		tmpa=cnta[an[i]-i+num];
    	}
    	else {
    		cnta[an[i]-i]++;
    		tmpa=cnta[an[i]-i];
    	}
    	if (bs[i]-i<0) {
    		cntb[bs[i]-i+num]++;
    		tmpb=cntb[bs[i]-i+num];
    	}
		else {
			cntb[bs[i]-i]++;
			tmpb=cntb[bs[i]-i];
		}
    	tmp=max(max(tmpa, tmpb), tmp);
    }
    ans=num-tmp;
    cout << ans;
    return 0;
}

总结:虽然题目有点坑,不过还是不错的题。

你可能感兴趣的:(算法竞赛)