首页 > 其他分享 >题解:TopCoder12316 ThreeColorability

题解:TopCoder12316 ThreeColorability

时间:2024-10-02 22:13:10浏览次数:5  
标签:ThreeColorability return 格子 fa TopCoder12316 题解 int 染色 find

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;
}

标签:ThreeColorability,return,格子,fa,TopCoder12316,题解,int,染色,find
From: https://www.cnblogs.com/tai-chi/p/18445164

相关文章

  • 树上最值路径 题解
    题意简述给你一棵\(n\)个结点的树,编号为\(1\simn\),求有多少路径\(\operatorname{Path}(u\rightarrowv)\),满足\(u=\max\{x\midx\in\operatorname{Path}(u\rightarrowv)\}\),\(v=\min\{x\midx\in\operatorname{Path}(u\rightarrowv)\}\)。......
  • 题解:CF2014D Robert Hood and Mrs Hood
    差分,每次差分将\(\max(1,l-d+1)\)加\(1\),将\(r+1\)位置减\(1\)。然后前缀和求原数组,再遍历一下求最小值和最大值即可。代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){ intt; cin>>t; while(t--){ intn,d,k; cin>>n>>d>>k;......
  • 题解:CF2009E Klee's SUPER DUPER LARGE Array!!!
    设\(m\)为\(a_1+\dots+a_i\),\(n\)为\(a_{i+1}+\dots+a_n\)。我们可以使用二分查找来搜索\(i\),使得\(m-n\)为最小的负数。如果我们移动到\(i+1\),则此时\(m-n\)为最小的整数。答案是两种情况下的最小绝对值。代码:#include<bits/stdc++.h>usingnamespacestd;pair<......
  • 题解:CF2009D Satyam and Counting
    比较容易观察的一道题,但是场上不开longlong见祖宗了。由于这题的\(x\)最大值比较小,所以我们可以直接存每个坐标是否有点。有两种三角形符号条件:存在两个点\((x,0),(x,1)\),可以观察到任意的其它点都可以成为第三点。有三个点为\((x,0),(x+1,1),(x+2,0)\)或\((x,1),(x+1......
  • 题解:CF2009C The Legend of Freya the Frog
    比较一眼的题目,场切了。分别考虑\(x\)和\(y\)。在\(x\)方向上我们需要的跳跃次数是\(\lceil\frac{x}{k}\rceil\),在\(y\)方向上我们需要的跳跃次数是\(\lceil\frac{y}{k}\rceil\)。考虑下面的两种情况:\(\lceil\frac{y}{k}\rceil\geq\lceil\frac{x}{k}\rceil......
  • 题解:P11137 [APC001] B - Checker
    注意到每个字符串长度相同,所以我们可以按照题意逐个遍历小K的题目和所有题库里的题目,统计相同位置字符相同的个数,如果大于\(\left\lceil\frac{k}{2}\right\rceil\),这两个题目就是重题。代码:#include<bits/stdc++.h>usingnamespacestd;boolc(stringa,stringb){in......
  • 题解:CF2014C Robin Hood in Town
    分享一种二分答案做法。先特判\(n=1\),\(n=2\),开始就有超过一半的人不高兴的情况。然后二分\(x\),计算新的和,新的平均值,如果有超过一半的人不高兴,就更新答案。代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){ intt; cin>>t; while(t--){ intn; cin>>n......
  • 题解:SP7973 ACPC10E - Sometimes, a penalty is good!
    比较简单的一道数学题。思路:计算小组赛的比赛总数。longlongstage1=G*T*(T-1)/2;每组有\(T\)个队伍,每个队伍都需要与其他\(T-1\)个队伍比赛,共有\(T\cdot(T-1)\)场比赛。共有\(G\)组,因此小组赛总比赛数为\(\frac{G\cdotT\cdot(T-1)}{2}\)。计算进入......
  • 题解:SP10242 ACPC11D - Dice on a Board
    思路递归生成所有的可能的筛子朝向,用DFS标记所有可达的位置,用dijkstra计算从起始位置到目标位置的最优路径,并确定在移动过程中能够获得的最大分数。generate函数generate用于生成所有可能的骰子朝向排列,\(mask\)作为参数,用于表示哪些数字已经被使用。使用二进制压缩。......
  • 题解:P9954 [USACO20OPEN] Cowntact Tracing B
    考虑暴力。枚举让每头牛都当一次“零号病人”和\(K\)的所有组合,模拟感染的过程,检查得出的病人是否和给出的一样即可。代码:#include<bits/stdc++.h>usingnamespacestd;boolinfectedd[101];intN,cowx[251],cowy[251];boolcheck(intpatient_zero,intK){ boolinfect......