(本人本题完成于2016-7-20)
题目大意:有一些点以1,2,3,...,n(3≤n≤50000)的顺序排成一个环,要求将其中一些点进行置换,使得置换后排成的环满足给定的条件:第1个点与第a1,b1个点相邻,第2个点与第a2,b2个点相邻,...,第n个点与第an,bn个点相邻。如果可以满足条件,求出至少要置换多少个点,否则输出-1。
做法:我们可以转换一下思路,要求至少要置换多少个点,可以先求至多可以不置换多少个点。首先按照给定的条件构成一个环,并对每个点的度数进行累加。我们知道,一个环内的节点度数都是2,在累加中,如果发现一个点的度数超过2,则一定无法满足条件。但这里还有一个情况,倘若按照给定条件形成了多个环,依照题目定义也是无法满足条件的,因此要进行特殊判断。构成环后,以标号为1的点为起始点遍历环,可以得到正反两个序列,可以发现这两个序列在经过一定的旋转操作后(别忘了这是一个环)是正好相反的。将这两个序列分别与1,2,3,...,n这个序列做差,如果结果小于0就加上n,就可以得到一个数列r。容易想到,如果r[i]=r[j],就表示经过一定的旋转操作后i和j可以同时落在自己的位置上,于是,我们只要求出最多有多少个r[i]相同,就是至多可以不置换的点数。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,con[50010][2]={0},deg[50010]={0},q[50010]={0},v[50010]={0};
int ans=99999999;
void work()
{
int x,m=0;
memset(v,0,sizeof(v));
for(int i=1;i<=n;i++)
{
x=q[i]-i;
if (x<0) x+=n;
v[x]++; //v[x]记录r[i]=x的点的数目
if (v[x]>m) m=v[x];
}
if (n-m<ans) ans=n-m;
}
void reverse()
{
int t;
for(int i=1;i<n-i+1;i++)
{
t=q[i];
q[i]=q[n-i+1];
q[n-i+1]=t;
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d %d",&con[i][0],&con[i][1]);
deg[con[i][0]]++;deg[con[i][1]]++; //累加度数
if (deg[con[i][0]]>2||deg[con[i][1]]>2||con[i][0]==i||con[i][1]==i) //判断无解情况
{printf("-1");return 0;}
}
for(int i=0,j=1;;) //求出其中一个序列
{
v[j]=1;i++;q[i]=j;
if (!v[con[j][0]]) j=con[j][0];
else if (!v[con[j][1]]) j=con[j][1];
else if (i!=n) {printf("-1");return 0;} //如果序列内的点数还没达到n,而已经无法再扩展,则判断为有多个环,无解
else break;
}
work(); //做差
reverse(); //反转序列
work(); //再一次做差
printf("%d",ans);
return 0;
}