问题描述

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

例如:点个数n=7,颜色m=3的涂色方案
在这里插入图片描述

算法设计

 一般连通图的可着色问题,不仅限于可平面图。

 给定图G=(V,E)和m种颜色,如果该图不是m可着色,给出否定回答;若m可着色,找出所有不同着色方法。


算法思路

设图G=(V, E), |V|=n, 颜色数= m, 用邻接矩阵a表示G, 用整数1, 2…m来表示

m种不同的颜色。顶点i所着的颜色用x[i]表示。

问题的解向量可以表示为n元组x={ x[1],…,x[n] }. x[i]∈{1,2,…,m},

解空间树为排序树,是一棵n+1层的完全m叉树.

在解空间树中做深度优先搜索, 约束条件:如果a[j][i]=1 , x[i] ≠ x[j]

    在这里插入图片描述         在这里插入图片描述

m=3,n=3时的解空间树
在这里插入图片描述

再举个例子

对于下图,写出图着色算法得出一种着色方案的过程。

顶点数量n=4, 色数:m=3
在这里插入图片描述

m=4,n=3时的解空间树
在这里插入图片描述
X[1]←1 , 返回 true
X[2]←1, 返回false; X[2]←2, 返回 true
X[3]←1 ,返回false; X[3]←2, 返回false;X[3]←3, 返回 true
X[4]←1, 返回false; X[4]←2, 返回false;X[4]←3, 返回 true

着色方案:(1,2,3,3)



复杂度分析

图m可着色问题的解空间树中,内结点个数是:
                  在这里插入图片描述
对于每一个内结点,在最坏情况下,用ok检查当前扩展结点每一个儿子的颜色可用性需耗时O(mn)。

因此,回溯法总的时间耗费是
在这里插入图片描述

算法

#include <bits/stdc++.h>

using namespace std;

int m;      //色数
int pointnum;  //点数
int edgenum;  //边数
int sum = 0;  //符合条件的着色方案数量
int Graph[100][100];  //各点之间的关系  1:两点连接 0:两点断开
int x[100];      //存储各点的着色情况


void InPut()
{
    int pos1, pos2;  //起始点
    cout<<"请输入点的个数和色数(p m):";
    cin>>pointnum>>m;

    cout<<"请输入边的个数: ";
    cin>>edgenum;
    memset(Graph, 0, sizeof(Graph));

    cout<<"输入边的起始点信息(起点 终点):"<<endl;
    for(int i = 1; i <= edgenum; ++i)
    {
        cin>>pos1>>pos2;
        Graph[pos1][pos2] = Graph[pos2][pos1] = 1;
    }
}

//判断当前点的着色是否合法合法, i 就是递归层数,也表明已经着色的节点索引
bool IsOk(int i)
{
    for(int j = 1; j < i; ++j)
        //两点如果相通并且与周围点的着色相同,就结束判断,表明当前的颜色选择不合法。
        if(Graph[i][j] == 1 && x[j] == x[i])
            return false;
    return true;
}

//核心代码
void BackTrack(int i)
{
    if(i > pointnum)
    {
        sum += 1;
        cout<<"方法 "<<sum<<"  :";
        for(int j = 1; j <= pointnum; ++j)
        {
           cout<<x[j];
        }
        cout<<endl;
        return;
    }
    else
    {
        for(int j = 1; j <= m; ++j)
        {
            x[i] = j;
            if(IsOk(i))
                BackTrack(i + 1);
            x[i] = 0;
        }
    }
}
void OutPut()
{
   cout<<"一共有 "<<sum<<" 种绘色方案"<<endl;
}
int main()
{
    InPut();
    BackTrack(1);
    OutPut();
}

测试样例

输入

请输入点的个数和色数(p m):5 4
请输入边的个数: 8
输入边的连接信息(点1 点2):
1 3
1 2
1 4
2 3
2 4
2 5
3 4
4 5

输出

方法 1 : 1 2 3 4 1
方法 2 : 1 2 3 4 3
方法 3 : 1 2 4 3 1
方法 4 : 1 2 4 3 4
方法 5 : 1 3 2 4 1
方法 6 : 1 3 2 4 2
方法 7 : 1 3 4 2 1
方法 8 : 1 3 4 2 4
方法 9 : 1 4 2 3 1
方法 10 : 1 4 2 3 2
方法 11 : 1 4 3 2 1
方法 12 : 1 4 3 2 3
方法 13 : 2 1 3 4 2
方法 14 : 2 1 3 4 3
方法 15 : 2 1 4 3 2
方法 16 : 2 1 4 3 4
方法 17 : 2 3 1 4 1
方法 18 : 2 3 1 4 2
方法 19 : 2 3 4 1 2
方法 20 : 2 3 4 1 4
方法 21 : 2 4 1 3 1
方法 22 : 2 4 1 3 2
方法 23 : 2 4 3 1 2
方法 24 : 2 4 3 1 3
方法 25 : 3 1 2 4 2
方法 26 : 3 1 2 4 3
方法 27 : 3 1 4 2 3
方法 28 : 3 1 4 2 4
方法 29 : 3 2 1 4 1
方法 30 : 3 2 1 4 3
方法 31 : 3 2 4 1 3
方法 32 : 3 2 4 1 4
方法 33 : 3 4 1 2 1
方法 34 : 3 4 1 2 3
方法 35 : 3 4 2 1 2
方法 36 : 3 4 2 1 3
方法 37 : 4 1 2 3 2
方法 38 : 4 1 2 3 4
方法 39 : 4 1 3 2 3
方法 40 : 4 1 3 2 4
方法 41 : 4 2 1 3 1
方法 42 : 4 2 1 3 4
方法 43 : 4 2 3 1 3
方法 44 : 4 2 3 1 4
方法 45 : 4 3 1 2 1
方法 46 : 4 3 1 2 4
方法 47 : 4 3 2 1 2
方法 48 : 4 3 2 1 4

一共有 48 中绘色方案
在这里插入图片描述

在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐