题目传送门
题目描述
现在有一个
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
1≤T≤5
每组测试数据有相同的结构构成:
每组数据第一行一个整数
N
N
N,表示网格大小,其中
1
≤
N
≤
50
1\le N\le 50
1≤N≤50。
之后有一个
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;
}