地图的四着色 中南1508

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");
       }*/
   }
}

代码:
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值