【题解】51nod 1388 六边形平面

本文介绍了如何解决51nod 1388题的六边形网格上色问题,通过建图将问题转化为图的着色问题。提到在六边形平面中,最小着色数最多为3。文章重点讨论了奇数环的概念,奇数环需要至少3种颜色来着色,并给出了判断奇数环的黑白涂色方法。
摘要由CSDN通过智能技术生成

题目传送门
题目描述
现在有一个 N × N N\times N N×N 的六边形网格平面(这种平面类似蜂窝形状)。下图是 N = 1 , 2 , 3 , 4 N=1,2,3,4 N=1,2,3,4 条件下的具体形状,根据它们可以依次类推 N = 5 , 6 , ⋅ ⋅ ⋅ N=5,6,··· N=5,6,
蜂窝巢
现在你需要对 N × N N\times N N×N 网格中一些格子进行上色,在给定的输入中这些格子被标记上字符 X \texttt{X} X,而不用上色的网格被标记为 - \texttt{-} -。上色时需要注意,如果两个被上色的格子有公共边,那么这两个格子需要被涂成不同的颜色。问最少需要多少种颜色来完成任务?

输入数据
多组测试数据,第一行一个整数 T T T,表示测试数据数量, 1 ≤ T ≤ 5 1\le T\le 5 1T5
每组测试数据有相同的结构构成:
每组数据第一行一个整数 N N N,表示网格大小,其中 1 ≤ N ≤ 50 1\le N\le 50 1N50
之后有一个 N × N N\times N N×N的字符矩阵A,其中 A i , j A_{i,j} Ai,j X \texttt{X} X 表示网格中坐标为 ( i , j ) (i,j) (i,j) 的格子需要被上色,否则不用。

输出数据
每组数据一行输出,即最少需要的颜色数量。

输入样例

3
3
---
---
---
4
-X--
---X
----
-X--
4
XXXX
---X
---X
---X

输出样例

0
1
2

首先可以确定的是,在六边形平面上的最小着色数顶多为 3 3 3。也就是说,不管怎么样,答案肯定是 0 0 0 1 1 1 2 2 2 或者 3 3 3
然而在一个六边形平面上实在不好操作,所以我们只好

建图!

(当然也可以不建,只是我想建)
只要是相邻的 X \texttt{X} X,那么就在他们之间连一条边。于是平面的着色问题就变成了经典的图的着色问题。
显然地:
没点啥颜色都不用;
有个点就要用 1 1 1 种颜色;
有条边就要用 2 2 2 种颜色;

3 3 3 种颜色呢???

奇数环!

什么是奇数环?奇数环就是一个环,上面的节点数为奇数。就像下面这样
在这里插入图片描述
对于奇数环,你不想用 3 3 3 种颜色,你也得用 3 3 3 种颜色。
那么怎么判奇数环?
从一个点开始黑白涂色,只要出现一条边连接着相同颜色的点的情况,直接判定为奇数环。
代码如下

#include<bits/stdc++.h>
using namespace std;
vector<int> graph[2505];
int num[55][55],color[2505];//num为该点在图中的序号,color为该点的颜色。
char str[55][55];
void dfs(int x,int y)//dfs涂色
{
	if(color[x]!=-1) return;//涂过就走人
	color[x]=y;//上色
	if(y) y=0;//手动翻转颜色
	else y=1;
	for(int i=0;i<graph[x].size();i++) dfs(graph[x][i],y);//扩展涂色
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		memset(num,0,sizeof(num));//一定要初始化!!!我就坑在这里了~~~
		memset(str,0,sizeof(str));
		int n,t=0;
		scanf("%d",&n);
		for(int i=1;i<=2500;i++) graph[i].clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%s",str[i]+1);
			for(int j=1;j<=n;j++)//统计X的数目
				if(str[i][j]=='X')
					num[i][j]=++t;//给图里的点分配序号
		}
		if(t==0)//没点啥颜色都不用
		{
			printf("0\n");
			continue;
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(str[i][j]=='X')//建边
				{
					if(str[i-1][j]=='X') graph[num[i][j]].push_back(num[i-1][j]);
					if(str[i-1][j+1]=='X') graph[num[i][j]].push_back(num[i-1][j+1]);
					if(str[i][j-1]=='X') graph[num[i][j]].push_back(num[i][j-1]);
					if(str[i][j+1]=='X') graph[num[i][j]].push_back(num[i][j+1]);
					if(str[i+1][j-1]=='X') graph[num[i][j]].push_back(num[i+1][j-1]);
					if(str[i+1][j]=='X') graph[num[i][j]].push_back(num[i+1][j]);
				}
		memset(color,-1,sizeof(color));
		for(int i=1;i<=t;i++) dfs(i,0);//从每个点开始dfs涂色,可以涂到每一个连通块。
		bool flag=false;
		for(int i=1;i<=t;i++)
		{
			for(int j=0;j<graph[i].size();j++)
				if(color[i]==color[graph[i][j]])//一条边连接着相同颜色的点
				{
					printf("3\n");//直接判定为奇数环
					flag=true;
					break;
				}
			if(flag) break;
		}
		if(flag) continue;
		for(int i=1;i<=t;i++)
			if(graph[i].size())//有边就要用两种颜色
			{
				flag=true;
				break;
			}
		if(flag)
		{
			printf("2\n");
			continue;
		}
		printf("1\n");//只剩1了
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值