图着色问题
一、图着色问题
(1)图的m可着色判定问题
给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色。
(2)图的m可着色优化问题
若一个图最少需要m种颜色才能使图中每条边连接的2个顶点着不同颜色,则称这个数m为该图的色数。
二、m可着色判定问题的解法
【算法】
(1)通过回溯的方法,不断的为每一个节点着色,在前面cur-1个节点都合法的着色之后,开始对第cur-1个节点进行着色,
(2)这时候枚举可用的m个颜色,通过和第cur-1个节点相邻的节点的颜色,来判断这个颜色是否合法
(3)如果找到那么一种颜色使得第cur-1个节点能够着色,那么说明m种颜色的方案在当前是可行的。
(4)cur每次迭代加1,如果cur增加到N并通过了检测,说明m种颜色是可满足的。
(5)注意,这里只是要求判断m种颜色是否可满足,所以找到任何一种方案就可以了。
【代码实现】
#include<iostream> #include<cstring> using namespace std; const int maxn = 105; int G[maxn][maxn]; int color[maxn]; bool ans; int n,m,k; void init(){ ans = 0; memset(G, 0 , sizeof G); memset(color, 0 , sizeof color); } void dfs(int cur){ if(cur > n) { ans = 1; return; } for(int i=1; i<=m; i++){ //对cur结点尝试使用每一种颜色进行涂色 bool flag = 1; //cur之前的结点必被涂色 for(int j=1; j<cur; j++){ if(G[j][cur] == 1 && color[j] == i){ flag = 0;//只要有一个冲突都不行 break; } } //如果可以涂上i颜色,则考虑下一个结点的情况 if(flag){ color[cur] = i; dfs(cur + 1); } //如果到这一步第cur个结点无法着色,则返回探寻其他方案 else color[cur] = 0;//回溯 ; } } int main(){ while(cin>>n>>k>>m){ init(); for(int i=1; i<=k; i++){ int x,y; cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1); cout<<ans<<endl; } return 0; }
三、m可着色拓展
【问题】在上述基础上,求出m种颜色能够给图G涂色的总总方案数量
【算法】由于这个时候要求总方案数量,所以在找到一种可行方案后,总是进行回溯再搜索其他的解决方案,与上面不同,上面是只需要找出一种方案即可,所以如果找到了就不需要再回溯了,所以在这里只需要把回溯语句的位置写到dfs语句的后面即可。
【代码实现】
#include<iostream> #include<cstring> using namespace std; const int maxn = 105; int G[maxn][maxn]; int color[maxn]; int ans; int n,m,k; void init(){ ans = 0; memset(G, 0 , sizeof G); memset(color, 0 , sizeof color); } void dfs(int cur){ if(cur > n) { ans++; return; } for(int i=1; i<=m; i++){ //对cur结点尝试使用每一种颜色进行涂色 bool flag = 1; //cur之前的结点必被涂色 for(int j=1; j<cur; j++){ if(G[j][cur] == 1 && color[j] == i){ flag = 0;//只要有一个冲突都不行 break; } } //如果可以涂上i颜色,则考虑下一个结点的情况 if(flag){ color[cur] = i; dfs(cur + 1); color[cur] = 0;//回溯 } } } int main(){ while(cin>>n>>k>>m){ init(); for(int i=1; i<=k; i++){ int x,y; cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1); cout<<ans<<endl; } return 0; }
四、图的m可着色优化问题
【问题】给定无向图图G,顶点数N,边集E, 要求用最少的颜色使得图中每一个顶点都涂上颜色,要求有边直接相连的顶点不能涂同样的颜色,问最少需要多少种颜色?
【算法】
(1)一种朴素的想法就是在上述图着色的结论基础上,使用二分法求出所需要的最小颜色数,范围1~N。但是存在大量的重复计算,复杂度过高。
(2)可以在之前的DFS上加上一维颜色数,一边增加顶点数一边增加颜色数,留下能够使得1~N顶点都满足的最少的颜色数
【题目链接】分考场
题目大意是:有n个学生分考场,其中有k对学生认识,认识的学生不能在同一个考场,问最少需要多少个考场。
相当于有n个顶点,k条边,有边相连的顶点不能涂相同的颜色,问最少需要多少颜色。
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn = 105; const int inf = 0x3f3f3f3f; int n,k; int x,y; int room[maxn];//room[i]表示i号房间的当前人数 int stu[maxn][maxn];//stu[i][j]表示第i个房间的第 j个学生是谁 int G[maxn][maxn];//存图 int ans ; void init(){ memset(room , 0, sizeof room); memset(stu, 0, sizeof stu); memset(G, 0, sizeof G); ans = inf; } //表示给第cur个学生安排房间 void dfs(int cur , int tot){ if(tot >= ans) return ; //如果学生都安排好了,则记录这种方法所导出的ans if(cur > n){ ans = min(ans , tot); return ; } //如果还没安排好,则给cur找合适的房间 for(int i=1; i<=tot; i++){ //看第i号房间里的所有人是否与其冲突 int len = room[i]; bool flag = 1; for(int j=1; j<=len; j++){ if(G[stu[i][j]][cur]){ flag = 0; break; } } //如果找到房间了 就去安排下一个学生 if(flag){ room[i]++;//这个房间里多住了cur stu[i][room[i]] = cur; //继续深搜 dfs(cur+1, tot); //回溯 stu[i][room[i]] = 0; room[i]--; } } //如果这 tot个房间都不行,则需要新开间房给这个学生 room[tot+1]++; stu[tot+1][room[tot+1]] = cur; //继续深搜 dfs(cur + 1 , tot + 1); //回溯 stu[tot+1][room[tot+1]] = 0; room[tot+1]--; } int main(){ while(cin>>n>>k){ init(); for(int i=1; i<=k; i++){ cin>>x>>y; G[x][y] = G[y][x] = 1; } dfs(1,0); cout<<ans<<endl; } }