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
思路:这个题目确实有点难,刚开始做这题确实吓到了,突然做到一道较难的题,心态有点不好,去网上看了看思路,压根静不下心看,第二天早上再看看,其实也就那回事,所以我们遇到难题别急心态放轻松,静静。假如这个题目给你的条件是 给你n个点,m条边,要你来染色,这简单么,就只需要一个dfs就能搞定。而这个题就难在没给你图,要你自己建图。所以我们要建立图;我建立图的思路是用bfs向他边上扩展,先将字母变成数字,给国家编号,然后在来一次bfs将他们相邻的添加到邻接表中。 好!!~~~~现在我们有了图,问题就是,有四种颜色要你给这些点染色,但是相邻的点不能染相同的颜色,颜色1,2,3,4;最后两种颜色加起来染的次数不能超过5次。是不是用dfs()就可以搞定啦。这里的时限是20秒,不剪枝也能过,还有一个剪枝的方法就是,
那里是第一组数据,大家可以看到,男生第一次染色1在前和2在前的个数是一样的,女生第一次染色3在前和4在前也是一样的(第一次染色指的是第一次染,比如男生先染一次,女生再染一次,就叫男生第一次染,女生第一次染,不是男生给第一个国家染,女生给第一个国家染)。所以最后的结果*4就行,看代码吧#include<stdio.h> #include<string.h> #include<stdlib.h> #include<vector> #include<queue> using namespace std; struct edge { int from; int to; }; struct node { int x,y; int number; }; vector<edge>edges; vector<int>G[100];//最后邻接表存图 char a[100][100];//开始用来接受输入 int map[100][100];//存储将字母转换成数值的矩阵 int n,m; int count;//国家的个数 int color[100]; int vis[100][100]; int vis1[100][100]; long long int cnt; int dir[4][2]={1,0,-1,0,0,1,0,-1}; int jude(int x,int y) { if(x<1||x>n||y<1||y>m) return 0; return 1; } void bfs() { memset(map,0,sizeof(map)); queue<node>q; count=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(map[i][j]!=0) continue; char ch=a[i][j]; node aa={i,j}; q.push(aa); while(!q.empty()) { aa=q.front(); q.pop(); map[aa.x][aa.y]=count; for(int i=0;i<4;i++) { int xx=aa.x+dir[i][0]; int yy=aa.y+dir[i][1]; if(!jude(xx,yy)) continue; if(a[xx][yy]!=ch) continue; if(map[xx][yy]!=0) continue; node b={xx,yy}; q.push(b); } } count++; } } } void addedge(int x,int y) { edge a={x,y}; edges.push_back(a); G[x].push_back(edges.size()-1); } void bfs1() { memset(vis,0,sizeof(vis));//判重map矩阵 x&&y的坐标, memset(vis1,0,sizeof(vis1));//判重 建立边,因为bfs扩展的时候一个国家的区域比较大,可能会扩展到两次,但是这样就会有两条边,判断边 重 queue<node>q; node a={1,1,1}; vis[1][1]=1; q.push(a); while(!q.empty()) { a=q.front(); q.pop(); for(int i=0;i<4;i++) { int xx=a.x+dir[i][0]; int yy=a.y+dir[i][1]; if(!jude(xx,yy)) continue; if(map[xx][yy]!=map[a.x][a.y])//当扩展到数值不相等,则他们不在一个国家了; { if(!vis1[map[a.x][a.y]][map[xx][yy]])//当着两个国家还没有建立边 { vis1[map[a.x][a.y]][map[xx][yy]]=1; addedge(map[xx][yy],map[a.x][a.y]);} } if(vis[xx][yy]) continue;//已经访问了就没必要在进去了。 vis[xx][yy]=1; node b={xx,yy}; q.push(b); } } } void inint() { edges.clear(); for(int i=0;i<30;i++) G[i].clear(); } int fun(int u,int x) { for(int i=0;i<G[u].size();i++) { if(color[edges[G[u][i]].to]==x) return 1; } return 0; } void dfs(int n,int x,int c1,int c2,int c3,int c4)//n染色到第n个国家,x要染的色,c1,c2,c3,c4表示那四种颜色一共染了多少次 { color[n]=x; if(c3+c4>5) return ; if(fun(n,x)) return ;//fun 函数 判断与n号 相邻的国家有没有染x这种颜色; if(n==count-1) { if(c1&&c2&&c3&&c4)//当四种颜色都用到了 { cnt++; /*for(int i=1;i<=count-1;i++) printf("%d ",color[i]); printf("\n");*/ } return ; } dfs(n+1,1,c1+1,c2,c3,c4); if(c1) dfs(n+1,2,c1,c2+1,c3,c4);//男生第一次染色,保证置染颜色1 dfs(n+1,3,c1,c2,c3+1,c4); if(c3) dfs(n+1,4,c1,c2,c3,c4+1);//女生第一次染色保证先染颜色3 color[n+1]=0; } int main() { int xaa=1; while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;i++) { scanf("%s",a[i]+1); } inint(); bfs();//第一个bfs将字母变成数字 bfs1();//将数字变成邻接表 cnt=0; // printf("%d\n",count); dfs(0,0,0,0,0,0);//根据图求染色的方案 printf("Case %d: %d\n",xaa++,cnt*4); /* for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) printf("%d ",map[i][j]); printf("\n"); } for(int i=1;i<count;i++) { for(int j=0;j<G[i].size();j++) { printf("%d ",edges[G[i][j]].to); } printf("\n"); }*/ } }
代码: