1.问题描述
如果我们把地图上的每一个区域都退化成一个点,相邻的区域用连线连接起来,那么地图就变成了一个无向连通图,我们给地图着色就相当于给该无向连通图的每个点着色,要求有连线的点不能有相同的颜色。这就是典型的图的m着色问题。给定无向连通图G m种颜色,找出所有不同的着色方案,使相邻区域有不同的颜色。如以下例子:
该图一共有7个区域,分别是ABCDEFG,我们分别编号1-7,每一个区域用一个顶点表示,相邻的区域有连线,那么地图就转换成了一个无向连通图。
如果用3种颜色给该地图着色,那么该问题中每个结点所着的颜色都有3种选择,7个结点所着的颜色号组合是一个可行解,比如说:{1,2,3,2,1,2,3}。
2.算法设计
约束条件:
假设当前扩展结点处于解空间树的t层,那么1~t-1个结点的状态已经确定。接下来沿着扩展结点的第一个分支进行扩展,此时需要判断第t个结点的着色情况。第t个结点的着色要与前面t-1个结点种,与其中有边相连的结点的颜色要不一样,如果颜色一样,那么这个t结点就不能用这个颜色,要换一个颜色尝试。
比如说:
在前3个已经着色的结点中,4号结点和1号、3号结点有边相连,那么4号结点选择的颜色不可以和1、3号结点的颜色相同。
扩展结点沿着第一个分支扩展,判断约束条件,如果满足,则进入深一层继续搜索,如果不满足,则扩展生成的结点给剪掉,换下一个色号尝试。如果所有的色号都尝试完毕,该结点变成死结点,向上回溯到离其最近的活结点,继续搜索。搜索到叶子结点时,找到一种着色方案,搜索过程直到全部活结点都变成死结点为止。
3.源代码
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> using namespace std; const int MAXX=111;//设定边界 int map[MAXX][MAXX]; //邻接矩阵存储图 int x[MAXX]; //解分量,记录色号 int sum=0; //记录有多少种方案 int n; //顶点个数 int m;//边的个数 int color_nums; //颜色数量 void createmap() //创建邻接矩阵 { int u; //顶点1 int v; //顶点2 cout << "请输入边的个数m:"<< endl; cin >> m; memset(map,0,sizeof(map)); cout << "请输入有边相连的两个顶点u和v:"<< endl; for (int i=1;i<=m;i++) { cin >> u>>v; map[u][v]=1; map[v][u]=1; } } bool OK(int t) //判断色号是否相同的函数 { for(int j=1;j<t;j++) //判断现在扩展点t和前面t-1个顶点有没有相连的 { if(map[t][j]) //如果相连 { if(x[j]==x[t]) //且颜色一样 { return false; //返回false,代表需要换个色号尝试 } } } return true; //如果色号不一样就是true } void backtrack(int t) //回溯、递推函数 { if(t>n) //到达叶子节点 { sum++; //方案个数 cout << "第"<< sum << "种方案"<< endl; for (int i=1;i<=n;i++) { cout << x[i] << " "; } cout << endl; } else { for (int i=1;i<=color_nums;i++) //尝试别的色号 { x[t]=i; //记录色号 if(OK(t)) //如果色号没有撞 { backtrack(t+1); //向下递推 } } } } int main() { cout << "请输入结点个数n:"<< endl; cin >> n; cout << "请输入颜色的数量:"<< endl; cin >> color_nums; cout << "请输入用邻接矩阵存储的图:"<< endl; createmap(); backtrack(1); return 0; }