深搜 图的M着色问题

吉林大学大二学生 东北师范大学附属中学OJ jinxi20111 2019.05.16

题目描述

给定无向连通图G和M种不同的颜色,用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的2个顶点着不同的颜色,则称这个图是M可着色的。图的M着色问题是对于给定图G和M种颜色,找出所有不同的着色法。

对于给定的无向连通图G和M种不同的颜色,编程计算图的所有不同的着色法。

输入

第一行有3个正整数N,K和M,表示给定的图G有N个顶点和K条边,M种颜色。顶点编号为1,2……,N。接下来的K行中,每行有2个正整数U,V,表示图G的一条边(U,V)。

 数据范围:1<N<=100    1<K<=2500     1<M<=6

输出

不同的着色方案数

样例输入

5 8 4
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5

样例输出

48

分析过程

在东北师大附中的OJ上刚过的一道题

用将近一周烦恼,解决就用了15min

简述一下我 silly B 的烦恼过程


看到一道题,我习惯直奔数据范围

N为100,M为6 ,深搜量Nm最大为1W亿

OK,炸了

直接排除 直接打表和简单深搜


然后就是怀疑人生阶段(持续一周)

我在想出思路之前,基本上不会去查题解

于是我下意识去构建了图

图的存储方式,最基本的就是邻接矩阵和邻接表

在这道题中,显然很难取舍

那就分开考虑,首先先做邻接矩阵

一个100平方的矩阵显然不大。我就直接写了个二维数组。接下来的深搜过程可给老子烦坏了。

我的思路是,染好第一个色之后,给它相连的点一个无法染色的标识。

我可以说,当时我想的很美,因为这样做成功了的话,我的dfs里面就干干净净只有一个deep,并且代码量也会很小。

只能说,菜鸡你想的太美律师函警告

因为染色过程中,势必会出现多个点连接一个点,这个时候,如何取消染色(回溯)成了一个难以解决的问题。

谁做谁知道。至少当时我没做出来。

然后我就去做了个邻接表,打算用宽搜BFS去做。

然而,找前置点成了问题,即你根本不知道为什么不能染某个色。

代码不粘了,太二了,又臭又长又超时。

两个都解不了。

懵了。

于是我去北苑一公寓三楼西北角的洗手间的第三个坑蹲了一会

有点便秘,没蹲出来(双关?)

没办法,暂时放弃。

在将近一周之中,我想了很多办法。比如,将边进行排序,用来节省生成邻接表的时间。(后来发现这很智障

又比如,生成树,然后逐层染色。结果仔细读读题,根本没说没有回路,,也就说有可能改不成树。

今天实验课,我闲得没事,在北苑一公寓三楼西北角的洗手间的第三个坑蹲坑的时候,把题拿了出来。

艹,这题跟怎么存图没啥关系,,


其实这道题的优化是有一些的。但是思路对了,应该是用不上优化的。

我们需要做的是,把图存到初始化为False的邻接矩阵中,然后用回溯法去遍历Color

回溯遍历?

如下:

代码一,条件分别代表 相连 同色 已染色

//回溯条件 相连 同色 已染色
if ((g[deep][j])&&(c[deep]==c[j])&&(c[deep]!=0))
{
	flag=false;
}

代码二,回溯行为,取消染色

if (flag)
{	
	dfs(deep+1);
}
c[deep]=0;

这样做,其实纯粹是灵光乍现。因为我一直觉得这样做既浪费时间,也浪费空间。但在这道题里,很巧妙的在空间和时间中找到了一个平衡点。深搜100个点,找点的过程是6*1002 ,不大,存边的矩阵是1002 ,也不大。可以说是非常完美(牛气叉腰!)

下面是AC代码展示

//吉林大学大二学生 东北师范大学附属中学OJ jinxi20111 2019.05.07

//MythCoffee 图的M着色问题
#include<iostream>
using namespace std;
int n,k,m,ans;
int g[101][101],c[101];
void dfs(int deep)
{
	if (deep>n)
	{
		ans++;
	}
	else
	{
		int i,j;
		bool flag;
		for(i=1;i<=m;i++)
		{
			c[deep]=i;
			flag=true;
			j=1;
			while ((j<=n)&&(flag))
			{
				if ((g[deep][j])&&(c[deep]==c[j])&&(c[deep]!=0))
				{
					flag=false;
				}
				j++;
			}
			if (flag)
			{	
				dfs(deep+1);
			}
			c[deep]=0;
		}
	}
	
}
int main()
{
	int s,j,t1,t2;
	cin>>n>>k>>m;
	for(s=1;s<=n;s++)
	{
		for(j=1;j<=n;j++)
		{
			g[s][j]=false;
		}
	}
	for(s=1;s<=k;s++)
	{
		cin>>t1>>t2;
		g[t1][t2]=true;
		g[t2][t1]=true;
	}
	dfs(1);
	cout<<ans;
	return 0;
}	
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值