图着色问题解析(贪心+回溯法)

问题:给定无向连通图G=(V,E),求最小的整数m,用m种颜色对图G的顶点着色,使得任意的两个相邻顶点着色不同

比如对于上面这个图,显然最小的颜色有2种 :1 3 4一种颜色,2,5一种颜色,那么对于贪心算法和回溯算法,我们要怎么来求解这个问题呢?首先来说一下贪心算法:

贪心算法

对于贪心算法,其中一种显然的贪心策略就是用一种颜色,尽可能多的为多个顶点去上色,例如对于上图 我们就有以下的步骤

① 对1上色k1并且flag = true,对2上色k1,发现1的颜色和2一样,取消上色并且flag = false,继续对34上色k1 ,满足条件,上色k1,对5继续上色k1,3 4结点和他相邻且颜色一样,取消上色并且flag = false

②取第二种颜色k2(k++并且flag = true,继续从1开始判断,发现1有颜色了,continue,判断2,对2上色,符合条件,判断34有色,continue,判断5,对5上色,ok,全部颜色上色完毕

实现贪心算法

#include<bits/stdc++.h>
using namespace std;
int edge[100][100],color[100];
//贪心算法
int drawcolor(int n){
	int k = 0;
	bool flag = true;
	while(flag){
		k++;//取下一种颜色
		flag = false;//假设全部上色了
		for(int i = 1;i<=n;i++){
			if(color[i]!=0) continue;//已经上色了
			color[i]=k;
			for(int j = 1;j<=n;j++)
			{
				if(edge[i][j]==1&&color[j]==k) 
				{
					color[i]=0;//取消上色
					flag = true;
					break;
				}
				//邻边颜色相同
			}
		}
	}
	return k;
}
int main()
{
    int n,n2,x,y;//边数
    cin>>n>>n2;
    for(int i = 0;i<n2;i++)
    {
		cin>>x>>y;
		edge[x][y] = 1;
		edge[y][x] = 1;
	}
	cout<<"贪心算法得到有结果"<<drawcolor(n)<<"种";
}

但是贪心算法得到的结果依然不是最佳的解法,比如对于下图: ​​​​​​​

这是一个二部图,我们使用以上贪心算法得到的结果就不会是最佳结果,我们会得到4钟颜色,但是其实最佳结果是2钟(实际上二部图都只需要2种颜色就可以完成上色,奇偶不同色即可),所以贪心算法得到结果并不是最好的,这里我们就需要使用回溯法

回溯法

首先给出m种颜色,要求对n个顶点上色,比如下面这张图

我们可以用空间树,画出下面这样的图,先对A上色k1,然后尝试对B上色,直到可以为止,然后继续对C,D,E

如果有子树都不行的情况,就回溯到上个上个结点,如8的子树都不行,就回溯到7,对7继续画出空间树

代码实现:

#include<bits/stdc++.h>
using namespace std;
int edge[100][100],color[100];
//回溯算法
bool panduan(int i,int n){
	for(int j = 1;j<=n;j++){
		if(edge[i][j]==1&&color[j]==color[i])
		return false;
	}
	return true;
}
void drawcolor(int n,int m){
	memset(color,0,sizeof(0));
	for(int i = 1;i<=n;){
		color[i]++;//取下一种颜色
		while(color[i]<=m&&!panduan(i,n))
			color[i]++;
			if (color[i]>m) {
				color[i] =0;
				i--;//回溯
							}
			else if(i<n) i++;//开始尝试为下一个上色
			else {
				//i==n
				for(int k = 1;k<=n;k++)
				cout<<"节点"<<k<<"的颜色是"<<color[k]<<endl;
				return ;
			}
			
			
	}

}

int main()
{
    int n1,n2,m,x,y;
    cin>>n1>>n2>>m;
    for(int i = 0;i<n2;i++)
    {
		cin>>x>>y;
		edge[x][y] = 1;
		edge[y][x] = 1;
	}
	drawcolor(n1,m);
}

对于上图样例 

对于上图二部图样例  发现确实只需要两种颜色就可以完成对二部图的涂色问题

做题感悟:发现对于需要回溯法的题目,他们都有相似的模板 ,很多问题我们只需要套用下面模板就可以完成啦

for(int i = 0;i<=n;){
//
//code段
if(不符合的条件)回溯
else if(满足条件) i++
else {
//i= n
输出or返回
}
}

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回溯法(Backtracking)是一种暴力搜索算法,它尝试在所有可能的解寻找正确的解。回溯法通常用于组合问题,其我们尝试从一组可能的解选择一个最佳解。 地图着色问题是指在地上给不同的区域着不同的颜色,使得相邻的区域颜色不同。这是一个经典的组合问题,可以使用回溯法解决。 下面是 Java 代码实现: ```java public class MapColoring { // 地邻接矩阵 private int[][] map; // 区域颜色 private int[] color; // 区域数量 private int n; public MapColoring(int[][] map, int n) { this.map = map; this.n = n; this.color = new int[n]; } public void colorMap() { if (backtrack(0)) { printSolution(); } else { System.out.println("No solution exists."); } } private boolean backtrack(int area) { // 如果所有区域都已经着色,返回 true if (area == n) { return true; } // 尝试给当前区域着色 for (int c = 1; c <= 4; c++) { if (isSafe(area, c)) { color[area] = c; // 递归着色下一个区域 if (backtrack(area + 1)) { return true; } // 如果不能着色,则回溯 color[area] = 0; } } return false; } private boolean isSafe(int area, int c) { // 检查相邻区域是否有着相同的颜色 for (int i = 0; i < n; i++) { if (map[area][i] == 1 && c == color[i]) { return false; } } return true; } private void printSolution() { System.out.println("Solution exists:"); for (int i = 0; i < n; i++) { System.out.println("Area " + (i + 1) + " is colored with " + color[i]); } } } ``` 在上述代码,`backtrack()` 方法用于尝试给当前区域着色,并递归着色下一个区域,如果不能着色,则回溯。`isSafe()` 方法用于检查相邻区域是否有着相同的颜色。`printSolution()` 方法用于输出结果。 如果地可以被正确着色,则 `colorMap()` 方法将打印结果。否则,它将打印“无解”。 下面是一个例子: ```java public static void main(String[] args) { int[][] map = { {0, 1, 1, 1}, {1, 0, 1, 0}, {1, 1, 0, 1}, {1, 0, 1, 0} }; MapColoring mc = new MapColoring(map, 4); mc.colorMap(); } ``` 输出结果为: ``` Solution exists: Area 1 is colored with 1 Area 2 is colored with 2 Area 3 is colored with 3 Area 4 is colored with 2 ``` 在这个例子,地可以被正确着色,其每个区域都着有不同的颜色。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值