求解图的m着色问题
问题描述
给定无向连通图G=(V, E)和m种不同的颜色,用这些颜色为图G的各顶点着色,每个顶点着一种颜色。如果有一种着色法使G中每条边的两个顶点着不同颜色,则称这个图是m可着色的。图的m着色问题是对于给定图G和m种颜色,找出所有不同的着色法。
输入描述:第1行有3个正整数n、k和m,表示给定的图G有n个顶点、k条边、m种颜色,顶点的编号为1、2、…、n。在接下来的k行中每行有两个正整数u 、v,表示图G的一条边(u 、v)。
输出描述:程序运行结束时将计算出的不同着色方案数输出。如果不能着色,则程序输出-1。
输入样例
5 8 4
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
样例输出:
共48种着色方案
第一个着色方案:…
第二个着色方案:…
……
问题分析
-
什么是回溯法?
回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。
例如:
顶点数n=7,颜色数m=3.按照1,2,3,4,5,6,7顺序构造搜索树.按深度优先策略可以得到第一个着3色方案(图中粗线路径所示)是: 顶点1、3和5着色1; 顶点2和6着色2; 顶点4和7着色3.在<1,2>的路径上只有这一个可行解.在<1,3>的路径上也只有一个可行解:顶点1、3和5着色1;顶点4和7着色2;顶点2和6着色3.其实这个解只是将第一个解中的颜色⒉和颜色3交换而已.根据对称性,在这棵树中只需搜索1/3的空间即可,搜索算法共可得到6个解.
import numpy as np
def same_color(i):
for j in range(1, n + 1):
if G[i][j] == 1 and x_is_color[i] == x_is_color[j]:
return False
return True
def show():
print(f'第{count}个着色方案:{x_is_color[1:n + 1]}')
def dfs(node):
global count
if node > n: # 达到叶子结点
count += 1 # 着色方案数增加1
show() # 输出方案
else:
for t in range(1, m + 1):
x_is_color[node] = t
if same_color(node): # 可以着色j 进入下一个顶点着色
dfs(node + 1)
x_is_color[node] = 0
if __name__ == "__main__":
count = 0
n = int(input("请输入顶点数:")) # n个顶点
k = int(input("请输入边数:")) # k条边
m = int(input("请输入颜色数:")) # m种颜色
G = np.zeros((n + 1, n + 1))
x_is_color = np.zeros(n + 1) # x[i]表示顶点i的着色
for i in range(k):
x, y = list(map(int, input("请输入一组整数用空格隔开:").split(" ")))
G[x][y] = 1
G[y][x] = 1
dfs(1)
if count > 0:
print(count)
else:
print(-1)
运行结果
时间复杂度分析
该算法中的每个顶点试探1 ~ m中着色,共n个顶点,对应的解空间树是一颗m叉树(子集树),算法的时间复杂度为 O ( n m n ) O(nm^n) O(nmn)。
java版本
import java.util.Arrays;
import java.util.Scanner;
public class Main {
int count = 0;
int n, m, k, G[][], x[];
public Boolean same_color(int i) {
for (int j = 1; j <= n; j++) {
if ((G[i][j] == 1) && (x[i] == x[j])) {
return false;
}
}
return true;
}
public void dfs(int node) {
if (node > n) {
count++;
System.out.println("第" + count + "个着色方案:" + Arrays.toString(x));
} else {
for (int i = 1; i <= m; i++) {
x[node] = i;
if (same_color(node)) {
dfs(node + 1);
}
x[node] = 0;
}
}
}
public void start() {
Scanner input = new Scanner(System.in);
System.out.print("请输入顶点数:");
n = input.nextInt();
System.out.print("请输入边数:");
k = input.nextInt();
System.out.print("请输入颜色数:");
m = input.nextInt();
G = new int[n + 1][n + 1];
x = new int[n + 1];
for (int i = 0; i < k; i++) {
System.out.print("请输入:");
int a = input.nextInt();
int b = input.nextInt();
G[a][b] = 1;
G[b][a] = 1;
}
dfs(1);
if (count > 0) {
System.out.println(count);
} else {
System.out.println(-1);
}
}
public static void main(String[] args) {
Main main = new Main();
main.start();
}
}