图的着色问题汇总

5 minute

二分图问题

能否只用 2 种颜色对一个图上色,并且使得共边顶点不同色?

代码解决:

 1vector<int> G[MAX_V];
 2int vertexes;
 3int edges;
 4int color[MAX_V];  //二分图,颜色为1或-1
 5bool dfs(int v, int c) {
 6  color[v] = c;
 7  for (int i = 0; i < G[v].size(); i++) {
 8    if (color[G[v][i]] == c) return false;
 9    if (color[G[v][i]] == 0 && !dfs(G[v][i], -c)) return false;
10  }
11  return true;
12}
13void solve() {
14  for (int i = 0; i < vertexes; i++) {
15    if (color[i] == 0) {
16      if (!dfs(i, 1)) {
17        printf("No\n");
18        return;
19      }
20    }
21  }
22  printf("Yes\n");
23}
24solve();

二分图是着色问题的特殊情况,即图的最小着色数为2,接下来的问题是:

图的最多着色方法数问题

对 V 个顶点,E 条边的图,用小于 num_color 种颜色进行涂色,要求共边的两点颜色不相同,一共有多少种涂法?

重要思想:回溯

代码解决:

 1int G[MAX][MAX]; // init: 0
 2int color[MAX]; // init: 0
 3int V, E, num_color, ccount = 0;
 4bool Judge_Color(int v, int col) { // 对v点用col继续涂色
 5  for (int i = 1; i <= V; i++)
 6    if (G[i][v] == 1 && color[i] == col) return false;
 7  return true;
 8}
 9void Solve(int v) {
10  if (v > V) { // 涂色完毕
11    ccount++;
12    for (int i = 1; i <= V; i++) cout << color[i] << " ";
13    cout << endl;
14    return;
15  }
16  for (int i = 1; i <= num_color; i++) {
17    if (Judge_Color(v, i)) { // 如果可以涂色则进行涂色
18      color[v] = i;
19      Solve(v + 1);
20      color[v] = 0; // 回溯
21    }
22  }
23}
24Solve(1); // 从第一个结点开始涂色

以上问题我们给定了最小的涂色数,求的是方法的个数,反过来呢?

现在我们想要用最少的颜色达到目标,就是要求一幅图的最小着色数了:

思路:从小到大增加着色数,判断以当前数量的颜色是否可以成功涂色,如果可以一一选取其中可以涂色的颜色进行涂色,如果不可以则新增颜色涂色

最小着色数问题

基本思想:剪枝与回溯

 1int G[MAX][MAX]; // init: 0
 2int color[MAX]; // init: 0
 3int V, E, c_min = MAX + 1;
 4bool Judge_Color(int v, int col) { // 判断v是否可以用col涂色
 5  for (int i = 1; i <= V; i++)
 6    if (G[i][v] == 1 && color[i] == col) return false;
 7  return true;
 8}
 9void Solve(int v, int num_color) {
10  if (num_color >= c_min) return;  //剪枝
11  if (v > V) { // 涂色完毕
12    c_min = min(c_min, num_color);
13    return;
14  }
15  for (int i = 1; i <= num_color; i++) {
16    if (Judge_Color(v, i)) { // 如果可以用这种颜色涂色
17      color[v] = i;
18      Solve(v + 1, num_color);
19      color[v] = 0;  //回溯
20    }
21  }
22  // num_color种颜色不够用了
23  color[v] = num_color + 1; // 新增一种颜色
24  Solve(v + 1, num_color + 1);
25  color[v] = 0; // 回溯
26}
27Solve(1, 1);

下面是有关最小着色问题相应实际问题的解决。

分考场问题

n 个人参加某项特殊考试。为了公平,要求任何两个认识的人不能分在同一个考场。求是少需要分几个考场才能满足条件。

 1int G[MAX][MAX]; // init 0
 2int room[MAX][MAX]; // 保存教室i中第j个学生的id, init: 0
 3int c_stu, c_relation, min_room = MAX + 1, count_room;
 4void solve(int x, int count_room) {
 5  if (count_room >= min_room) return; // 剪枝
 6  if (x > c_stu) { // 学生分配完毕
 7    min_room = min(min_room, count_room);
 8    return;
 9  }
10  for (int i = 1; i <= count_room; i++) {
11    int flag = true, j = 0;
12    while (room[i][j]) { //查找学生教室中是否有认识的人
13      if (G[x][room[i][j]]) {
14        flag = false;
15        break;
16      }
17      j++;
18    }
19    if (flag) { // 教室里没有认识的人
20      room[i][j] = x;
21      solve(x + 1, count_room); // 继续用现有教室分配下一个学生
22      room[i][j] = 0;
23    }
24  }
25  // 教室不够 新增一个教室并把学生放进教室
26  room[count_room + 1][0] = x;
27  solve(x + 1, count_room + 1);
28  room[count_room + 1][0] = 0;
29}
30
31solve(1, 1); // 最开始用一个教室分配学生