1508: 地图的四着色
Time Limit: 20 Sec Memory Limit: 128 Mb Submitted: 222 Solved: 102Description
有一个R行C列的网格地图,每个国家是一个四连通区域。你的任务是用红,绿,蓝,黄四种颜色给地图着色,使得相邻国家的颜色不同。
一个人着色比较无趣,所以你想请女朋友陪你一起涂——你涂红绿,她涂蓝黄。当然,绅士是不会让让女朋友受累的,所以她最多只需涂5个国家(恰好5个也行)。
你的任务是统计有多少种着色的方法。注意,每个颜色都至少要用一次。
Input
输入包含不超过100组数据。每组数据第一行为两个整数R和C (1<=R,C<=20),即网格的行数和列数。以下R行每行C个大写字母。相同字母所组成的四连通区域代表一个国家。输入保证国家数目不超过30,并且大多数测试点的国家数都比较小。
Output
对于每组数据,输出测试点编号和着色方案数。
Sample Input
2 4 AABB BBAA 1 5 ABABA 4 7 AABAABB ABBCCCB BBAACBB CCABBAC
Sample Output
Case 1: 24 Case 2: 144 Case 3: 3776
思路:
先用dfs,将四连通的相同字母的每一片编号,比如
AABB
BBAA
处理为
0011
2233
然后将每一片编号相同的区域作为一个以这个号码为编号的结点,构建邻接矩阵。
如果一个编号的四连通有其他编号,说明这两个结点连通,邻接。
得到邻接矩阵:
0110
1001
1001
0110
然后用搜索,尝试每一个结点使用每一种颜色(用邻接矩阵判断方案是否可行)
直接搜索会超时,我们尝试剪枝:
男使用AB颜色,女使用CD颜色。
如果我们强制规定,男使用的第一种颜色是A,女使用的第一种颜色是C,那么我们可以得到很大的时间上的优化。
而先使用哪个颜色,都是一样的。所以只需要在这基础上得到的结果乘以4就是最终答案。
AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
char maps[22][22];
int num[22][22];
int adjMa[33][33];
int color[33];
int ax[5]= {0,0,1,-1};
int ay[5]= {1,-1,0,0};
int r,c;
int n;
void dfs(int a,int b,int p)
{
num[a][b]=p;
for(int i=0; i<4; i++)
{
int x=a+ax[i];
int y=b+ay[i];
if(x>=0&&x<r&&y>=0&&y<c&&maps[x][y]==maps[a][b]&&num[x][y]==-1)
{
dfs(x,y,p);
}
}
}
int check(int step,int a)
{
for(int i=0;i<n;i++)
{
if(adjMa[step][i]==1&&color[i]==a) return 0;
}
return 1;
}
int dfsAns(int step,int A,int B,int C,int D)
{
int ans=0;
if(step==n) return ans+=A&&B&&C&&D;
if(check(step,0))
{
color[step]=0;
ans+=dfsAns(step+1,A+1,B,C,D);
}
if(A&&check(step,1))
{
color[step]=1;
ans+=dfsAns(step+1,A,B+1,C,D);
}
if(C+D<5&&check(step,2))
{
color[step]=2;
ans+=dfsAns(step+1,A,B,C+1,D);
}
if(C&&C+D<5&&check(step,3))
{
color[step]=3;
ans+=dfsAns(step+1,A,B,C,D+1);
}
color[step]=-1;
return ans;
}
int main()
{
int cake=1;
while(~scanf("%d%d",&r,&c))
{
getchar();
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
scanf("%c",&maps[i][j]);
}
getchar();
}
memset(num,-1,sizeof(num));
int p=0;
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
if(num[i][j]==-1)
{
dfs(i,j,p);
p++;
}
}
}
n=p;
memset(adjMa,0,sizeof(adjMa));
for(int i=0; i<r; i++)
{
for(int j=0; j<c; j++)
{
for(int k=0; k<4; k++)
{
int x=i+ax[k];
int y=j+ay[k];
if(x>=0&&x<r&&y>=0&&y<c&&num[x][y]!=num[i][j])
{
adjMa[num[i][j]][num[x][y]]=1;
adjMa[num[x][y]][num[i][j]]=1;
}
}
}
}
memset(color,-1,sizeof(color));
int ans=dfsAns(0,0,0,0,0);
printf("Case %d: %d\n",cake++,ans*4);
}
return 0;
}