Loading

题解:TopCoder12316 ThreeColorability

Vjudge

可以出成《三色绘恋》背景。

题意

给一个格点数为 \((n+1) \times (m+1)\) 的网格,给格点染色,相邻的格点不能染成同样的颜色。每个格子有一条对角线的边,可选 N 形和 Z 形。现在有一个残缺的网格,存在一些格子的对角线连法不确定,构造一种字典序最小的方案使得至少存在一种染色方法。

两种可能的连边及染色方案:

本例的输入和输出均为 Z

本例的输入为:

??
?N

输出为:

NN
NN

分析

手玩得出一个结论:田字形格子的连法只可能使得 NZ 的数量为偶数。这种结论可以扩展到一半的矩形上:一个矩形存在合法的染色方案,当且仅当四个角的 NZ 的数量为偶数

证明可以这样做:把 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;
}
posted @ 2024-10-02 22:08  tai_chi  阅读(10)  评论(0编辑  收藏  举报