题解 P1053 【篝火晚会】
这道题有难度,最难想到是的贪心。
假设初始数列为 1,2,3..n
目标序列 a1,a2,a3..an
贪心:假设能够构成环,那么置换的代价一定可以等于所有不在位置上的数的个数。
举个例子,序列中没在原先位置的数,一定可以换到它应在的位置,代价为1,而那个被取而代之的数,
也一定可以换到它应有的位置
(它原先一定不在自己的位置,因为一个位置的点只能有1个,而它已被某个点取到了)。
于是O(n^2)的算法诞生了,将初始序列的状态固定,枚举置换得到的序列,每次找一遍即可。
O(n)的优化,发现许多数都不会在位置上,这些状态都浪费了。
对于置换得到的数列,找每个数距离初始状态的长,统计相同长的个数即可。
#include<cstdio>
#define MAXN 50001
int a[MAXN], b[MAXN];
int s[MAXN], cnt1[MAXN], cnt2[MAXN],ans;
int i,j,n;
int main()
{
scanf("%d",&n);
for (i=1; i<=n; i++)
scanf("%d%d",&a[i],&b[i]);
s[1]=1; s[n]=a[1]; s[2]=b[1];
for (i=3; i<n; i++)
if (a[s[i-1]]==s[i-2]) s[i]=b[s[i-1]];
else s[i]=a[s[i-1]];
for (i=1; i<=n; i++)
{
int t1=i+1, t2=i-1;
if (t1>n) t1=1;
if (t2<1) t2=n;
if ((s[t1]!=a[s[i]] || s[t2]!=b[s[i]]) && (s[t2]!=a[s[i]] || s[t1]!=b[s[i]]))
{
printf("-1");
return 0;
}
}
for (i=1; i<=n; i++)
{
cnt1[(s[i]-i+n)%n]++;
cnt2[(s[i]+i-1)%n]++;
}
for (i=0; i<n; i++) //注意注意,%n后存放的地址为0..n-1。否则会爆第10个点
{
if (ans<cnt1[i]) ans=cnt1[i];
if (ans<cnt2[i]) ans=cnt2[i];
}
printf("%d",n-ans);
return 0;
}