回溯法:图的m着色问题(C++)

回溯法:图的m着色问题

  • 问题描述
  • 算法设计
    • 确定问题的解空间
    • 确定解空间的结构
    • 按深度优先搜索排列树,并用约束条件进行剪枝
  • 代码实现
  • 算法复杂度分析

问题描述

给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法,使G中每条边的2个顶点着有不同的颜色?这个问题是图的可着色判定问题。若一个图最少需要m种颜色才能使图中每条边连接的2个顶点着有不同的颜色,则称这个数m为该图的色数。求一个图的色数m的问题称为图的m可着色优化问题

算法设计

根据回溯法来进行算法设计

确定问题的解空间

用图的邻接矩阵a表示无向连通图G = (V , E)。若(I,j)属于图的边集,则a[i][j] = 1,否则a[i][j] = 0。整数1~m表示m种不同的颜色。顶点i所着的颜色用x[i]表示。数组x[1:n]是问题的解向量。问题的解空间就是这些解向量的集合。

确定解空间的结构

不难看出,该问题的解空间可以表示为一颗高度为n+1的完全m叉树,并且是排列树。解空间树的第i(1<=i<=n)层中的每个节点都有m个儿子,每个儿子对应x[i]的m个可能着色之一。第n+1层均为叶子结点。

按深度优先搜索排列树,并用约束条件进行剪枝

该问题的约束条件为:有边相邻的两个顶点的颜色不能相同

代码实现

// m着色问题
//需要自行实现图类和主函数
class Color {
	friend int mColoring(int, int, int **);
private:
	bool OK(int k);
	void Backtrack(int t);
	int n;		//顶点数
	int m;		//可用颜色数
	int **a;	//邻接矩阵
	int *x;		//解向量
	long sum;	//可着色方案数
};
//判断是否可被着色
bool Color::OK(int k) {
	for (int j = 1; j <= n; j++) {
		if ((a[k][j] == 1) && x[k] == x[j])
			return false;
	}
	return true;
}

void Color::Backtrack(int t) {
	//当访问到叶子结点,则找到一种可着色方案 sum++
	if (t > n) {
		sum++;
		for (int i = 1; i <= n; i++)
			cout << x[i] << " ";
		cout << endl;
	}
	else {
		for (int i = 1; i <= m; i++) {
			x[t] = i;
			if (OK(t))
				Backtrack(t + 1);
			x[t] = 0;
		}
	}
}

int mColoring(int n, int m, int **a) {
	//进行初始化操作
	Color X;		
	X.n = n;
	X.m = m;
	X.a = a;
	X.sum = 0;
	int *p = new int[n + 1];
	for (int i = 0; i <= n; i++)
		p[i] = 0;
	X.x = p;
	//从第一层开始递归
	X.Backtrack(1);
	delete[]p;
	return X.sum;
}
}

算法复杂度分析

图m可着色问题的解空间树中的节点个数为mi (i=0,1,2…n-1),对于每个节点最坏情况下检查的次数为m(n-1),即时间复杂度为O(mn),因此总时间为mi(m(n-1)) = O(nmn)。

你可能感兴趣的:(回溯法:图的m着色问题(C++))