链接:https://ac.nowcoder.com/acm/contest/886/E
题意:你需要构造一个n个结点的无向图,使该图的补图和原图形成同构关系。若可以,则输出该图的邻接矩阵以及各个结点映射后的结点号。若不能,输出NO。
思路情况:显然,两个图如果同构,那么它们的边数一定相等。如果n个结点的完全图边数之和不是偶数,那么自然不成立。于是我们可以明白只有两种情况可以成立,即
n
n
n%4
=
=
1
==1
==1或
n
n
n%4
=
=
0
==0
==0的情况才是成立的。
在判断是否可行后,我们可以开始构图。我的第一想法是直接强行构建一张图,只要这个图是对称关系,那么它的补图就一定和原图同构,但是很快我就发现,虽然这张图是同构的,但是我很难找到各个结点映射后的结点是哪个。所以这个方法不成立。
正解是将所有点4个一组分开,每组连成一条线,对于每组的中间两个点,和之前的所有点连线,这样构出来的图显然是正确的,首先,根据样例可以知,四个点构成的线可以自身同构,其次,每组中间的两个点和前面所有的点连接后,它的补图显然是剩下的两个点和所有点连接,自然同构。
这两个点不能随意选取,必须是四个点在原图中和补图中关系一致的点,即原图中选的是中间两个点,那么补图也要是中间两个点,只需要将四个点构成的线原图和补图画出来,就很容易发现可行点对不是很多,不能随意选点。(亲测,随意选点只能过10%的case)
#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
const int inf=0x3f3f3f3f;
int arr[maxn][maxn];
int color[maxn];
vector<int>vis[maxn],vis1[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin>>T;
int cnt=1;
while(T--)
{
int n;
cin>>n;
if(n%4!=0&&n%4!=1)
{
cout<<"Case #"<<cnt++<<": "<<"No"<<endl;
continue;
}
for(int i=1;i<=n;i++)color[i]=i;
memset(arr,0,sizeof(arr));
if(n%4==0)
{
for(int i=1;i<=n;i+=4)
{
for(int j=0;j<3;j++)
{
arr[i+j][i+j+1]=arr[i+j+1][i+j]=1;
}
for(int k=1;k<i;k++)
{
arr[i+2][k]=arr[k][i+2]=1;
arr[i+1][k]=arr[k][i+1]=1;
}
}
for(int i=1;i<=n;i+=4)
{
swap(color[i+1],color[i+2]);
swap(color[i],color[i+1]);
swap(color[i+2],color[i+3]);
}
}
else
{
for(int i=2;i<=n;i+=4)
{
for(int j=0;j<3;j++)
{
arr[i+j][i+j+1]=arr[i+j+1][i+j]=1;
}
for(int k=1;k<i;k++)
{
arr[i+2][k]=arr[k][i+2]=1;
arr[i+1][k]=arr[k][i+1]=1;
}
}
for(int i=2;i<=n;i+=4)
{
swap(color[i+1],color[i+2]);
swap(color[i],color[i+1]);
swap(color[i+2],color[i+3]);
}
}
cout<<"Case #"<<cnt++<<": "<<"Yes"<<endl;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cout<<arr[i][j];
}
cout<<endl;
}
for(int i=1;i<=n;i++)
{
cout<<color[i]<<(i==n?'\n':' ');
}
}
}