可以出成《三色绘恋》背景。
题意
给一个格点数为 \((n+1) \times (m+1)\) 的网格,给格点染色,相邻的格点不能染成同样的颜色。每个格子有一条对角线的边,可选 N
形和 Z
形。现在有一个残缺的网格,存在一些格子的对角线连法不确定,构造一种字典序最小的方案使得至少存在一种染色方法。
两种可能的连边及染色方案:
本例的输入和输出均为 Z
。
本例的输入为:
??
?N
输出为:
NN
NN
解答
手玩得出一个结论:田字形格子的连法只可能使得 N
和 Z
的数量为偶数。这种结论可以扩展到一半的矩形上:一个矩形存在合法的染色方案,当且仅当四个角的 N
和 Z
的数量为偶数。
证明可以这样做:把 N
看成 \(0\),把 Z
看成 \(1\)。这样 \(2 \times 2\) 的条件等价于四个格子的异或值为 \(0\)。这样向外扩展一层只需要联立两个相交的田形即可。
根据这个结论,我们可以以第一行为基础去推其他的行,理由是任意两行只可能是完全相同或完全相反。使用扩展域并查集将对应位置相同的行号合并,再将组内的信息分散填到每行。
如果填不了了就可以填个 N
到第一行继续做,保证字典序最小。
时间复杂度根据写法的不同可以做到 \(O(n^2)\) 或 \(O(n^3)\),笔者比较笨只能写三方复杂度。
#include <bits/stdc++.h>
using namespace std;
#define Fail { puts("0"); flag=1; return; }
const int N=505;
int n,m;
char a[N][N];
bool vis[N];
int flag;
int fa[N*2];
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void mrg(int x,int y)
{
x=find(x), y=find(y);
fa[x]=y;
}
queue<int> q;
void work(int u)
{
for(int i=2;i<=n;i++)
{
if(a[i][u]=='?') continue;
if(a[1][u]==a[i][u])
{
if(find(i+n)==find(1)) Fail;
mrg(1,i), mrg(1+n,i+n);
}
else
{
if(find(i)==find(1)) Fail;
mrg(1+n,i), mrg(1,i+n);
}
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(find(i)==find(1))
{
if(a[1][j]=='?'&&a[i][j]=='?') continue;
else if(a[1][j]=='?'&&a[i][j]!='?') {a[1][j]=a[i][j]; if(!vis[j]) q.push(j), vis[j]=1;}
else if(a[1][j]!='?'&&a[i][j]=='?') a[i][j]=a[1][j];
else if(a[1][j]!='?'&&a[i][j]!='?'&&a[1][j]!=a[i][j]) Fail;
}
else if(find(i+n)==find(1))
{
if(a[1][j]=='?'&&a[i][j]=='?') continue;
else if(a[1][j]=='?'&&a[i][j]!='?') {a[1][j]=(a[i][j]=='N'?'Z':'N'); if(!vis[j]) q.push(j), vis[j]=1;}
else if(a[1][j]!='?'&&a[i][j]=='?') a[i][j]=(a[1][j]=='N'?'Z':'N');
else if(a[1][j]!='?'&&a[i][j]!='?'&&a[1][j]==a[i][j]) Fail;
}
}
}
}
void solve()
{
scanf("%d%d",&n,&m);
for(int j=1;j<=m;j++) vis[j]=0;
while(!q.empty()) q.pop();
flag=0;
for(int i=1;i<=n;i++)
{
scanf("%s",a[i]+1);
}
for(int i=1;i<=n*2;i++) fa[i]=i;
for(int x=1;x<=n;x++)
{
for(int y=x+1;y<=n;y++)
{
for(int j=1;j<=m;j++)
{
if(a[x][j]=='?') continue;
if(a[y][j]=='?') continue;
if(a[x][j]==a[y][j])
{
if(find(x+n)==find(y)) Fail;
mrg(x,y), mrg(x+n,y+n);
}
else
{
if(find(x)==find(y)) Fail;
mrg(x,y+n), mrg(x+n,y);
}
}
}
}
for(int j=1;j<=m;j++) if(a[1][j]!='?') q.push(j);
while(!q.empty())
{
int u=q.front(); q.pop();
if(!vis[u])
{
vis[u]=1;
work(u);
if(flag) return;
}
}
for(int u=1;u<=m;u++)
{
if(a[1][u]=='?') a[1][u]='N';
work(u);
if(flag) return;
}
for(int i=2;i<=n;i++)
{
bool f=1;
for(int j=1;j<=m;j++) if(a[i][j]!='?') f=0;
if(f)
{
if(a[1][1]=='N') for(int j=1;j<=m;j++) a[i][j]=a[1][j];
else for(int j=1;j<=m;j++) a[i][j]=(a[1][j]=='N'?'Z':'N');
}
}
for(int i=1;i<=n;i++) printf("%s\n",a[i]+1);
}
signed main()
{
int T; scanf("%d",&T);
while(T--) solve();
return 0;
}
标签:ThreeColorability,return,格子,fa,TopCoder12316,题解,int,染色,find
From: https://www.cnblogs.com/tai-chi/p/18445164