链接:https://ac.nowcoder.com/acm/contest/886/E
题意:T组样例。每组样例给一个n,问能否构造一个图,使得其和其补图同构。能就输出“Yes”,并输出图的邻接矩阵和同构的映射函数,不能就输出“No”。
思路:图同构的必要条件为点的个数和边的个数都相同。显然,点数肯定相同,一个图和其补图合起来,就是n个点组成的n阶完全图,所以总边数为(n-1)*n/2。那么这个总边数还得是个偶数,这样原图和补图的边数才相同。也就是(n-1)*n/2/2是个整数,也就是(n-1)或n是4的倍数。即:n%4==0或n%4==1。
(1)n%4==0时,构造时,我们把点分成两份大小相同的集合,一个内部连成完全图,一个内部不连边,这样补图中,他俩就交叉对应。当然,他俩内部处理完了,还要给这两部分的点加边。方法就是再把这两部分等分,然后上半部分和上半部分相连,下半部分和下半部分相连,至于映射,两个集合交换一下就好了。
(2)n%4==1时,多出的一个点和其中一个集合的所有点相连,其余和n%4==0时的处理相同。请看图。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e3+10;
int G[N][N],f[N],n;
void Init()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
G[i][j]=0;
return ;
}
void work(int n)
{
int n2=n/2,n4=n/4;
//cout<<n2<<" "<<n4<<endl;
for(int i=1;i<=n2;i++)
for(int j=i+1;j<=n2;j++)
{
G[i][j]=1;
G[j][i]=1;
}
for(int i=1;i<=n4;i++)
for(int j=1;j<=n4;j++)
{
G[i][n2+j]=1;
G[n2+j][i]=1;
}
for(int i=1;i<=n4;i++)
for(int j=1;j<=n4;j++)
{
G[n4+i][n2+n4+j]=1;
G[n2+n4+j][n4+i]=1;
}
if(n&1)
{
for(int i=1;i<=n/2;i++)
G[i][n]=G[n][i]=1;
f[n]=n;
n--;
}
for(int i=1;i<=n2;i++)
f[i]=n-i+1;
for(int i=n2+1;i<=n;i++)
f[i]=i-n2;
return ;
}
int main(void)
{
int t,tt=0;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
Init();
printf("Case #%d: ",++tt);
if(n%4==2||n%4==3)
{
printf("No\n");
continue;
}
else
{
printf("Yes\n");
work(n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
printf("%d",G[i][j]);
printf("\n");
}
for(int i=1;i<=n;i++)
printf("%d%c",f[i],i==n?'\n':' ');
}
}
return 0;
}