1508: 地图的四着色
Time Limit: 20 Sec
Memory Limit: 128 Mb
Submitted: 230
Solved: 105
Description
有一个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
Hint
思路:先对图中进行bfs为每个国家编号,暴力去找出每个国家都与哪些国家相邻并建边,之后就产生了一个新图,就把问题转化为有多少种染色方案能使得相连的点的颜色两两互不相同。正解应该是二分图染色,不过用点组合数学加上dfs也是能比较快的跑出来。
组合思想:我们可以强行让男的第一次先涂第一种颜色,女的第一次先涂第三种颜色。(对于此题,先涂哪一个颜色都是等价的,所以结果是2*2倍)(来源于:
http://blog.csdn.net/acm_cxq/article/details/52224876)
要注意的是答案会爆int
Source
湖南省第十届大学生计算机程序设计竞赛#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct jj
{
int x,y;
};
struct kk
{
int v,next;
}w[900]; //新图
int h[30],numw,mpid[20][20],n,m,num,v[30],walk[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
long long sum;
char mp[20][21];
void insert(int u,int v)
{
w[numw].v=v;
w[numw].next=h[u];
h[u]=numw++;
}
void bfs(int x,int y)
{
int i;
struct jj s,pos;
queue<struct jj>q;
mpid[x][y]=num;
s.x=x;
s.y=y;
q.push(s);
while(q.empty()==0)
{
s=q.front();
q.pop();
for(i=0;4>i;i++)
{
pos.x=s.x+walk[i][0];
pos.y=s.y+walk[i][1];
if(pos.x>=0&&pos.x<n&&pos.y>=0&&pos.y<m&&mpid[pos.x][pos.y]==-1&&mp[x][y]==mp[pos.x][pos.y])
{
mpid[pos.x][pos.y]=num;
q.push(pos);
}
}
}
}
int judge(int deep,int kind)
{
int i;
for(i=h[deep];i!=-1;i=w[i].next)
{
if(v[w[i].v]==kind) //使用的颜色不符合条件
{
return 0;
}
}
return 1;
}
void dfs(int deep,int buff,int status)
{
int i;
if(deep==num)
{
if(status==15) //当状态为15的时候说明每一种颜色都用上了
{
sum++;
}
return;
}
if(judge(deep,1)==1)
{
v[deep]=1;
dfs(deep+1,buff,status|1);
v[deep]=0;
}
if(judge(deep,2)==1&&(status&1)!=0)
{
v[deep]=2;
dfs(deep+1,buff,status|2);
v[deep]=0;
}
if(buff==0)
{
return;
}
if(judge(deep,3)==1)
{
v[deep]=3;
dfs(deep+1,buff-1,status|4);
v[deep]=0;
}
if(judge(deep,4)==1&&(status&4)!=0)
{
v[deep]=4;
dfs(deep+1,buff-1,status|8);
v[deep]=0;
}
}
int main()
{
int i,i1,i2,i3,x,y;
for(i=1;scanf("%d %d",&n,&m)!=EOF;i++)
{
for(i1=0;n>i1;i1++)
{
scanf("%s",mp[i1]);
}
memset(mpid,-1,sizeof(mpid)); //每个点所属的国家编号
memset(h,-1,sizeof(h));
num=0;
sum=0;
numw=0;
for(i1=0;n>i1;i1++)
{
for(i2=0;m>i2;i2++)
{
if(mpid[i1][i2]==-1)
{
bfs(i1,i2); //bfs为国家编号
num++;
}
}
}
for(i1=0;n>i1;i1++)
{
for(i2=0;m>i2;i2++)
{
for(i3=0;4>i3;i3++) //暴力找相邻国家
{
x=i1+walk[i3][0];
y=i2+walk[i3][1];
if(x>=0&&x<n&&y>=0&&y<m&&mpid[i1][i2]!=mpid[x][y])
{
insert(mpid[i1][i2],mpid[x][y]);
}
}
}
}
memset(v,0,sizeof(v)); //初始化所有点都未上色
dfs(0,5,0);
printf("Case %d: %lld\n",i,sum*4);
}
return 0;
}