图的m着色问题-回溯法

问题描述

 给定无向连通图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 中绘色方案
在这里插入图片描述

在这里插入图片描述

  • 40
    点赞
  • 313
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一个C++实现的基于回溯法着色程序。以下是代码实现: ```c++ #include <iostream> #include <vector> using namespace std; // 着色 bool graphColoring(vector<vector<int>>& graph, vector<int>& color, int node, int numColors) { if (node == graph.size()) { // 所有节点着色完毕 return true; } for (int c = 1; c <= numColors; c++) { // 遍历所有颜色 bool validColor = true; for (int i = 0; i < graph.size(); i++) { if (graph[node][i] && color[i] == c) { // 邻接节点颜色相同 validColor = false; break; } } if (validColor) { // 当前颜色可用 color[node] = c; // 着色 if (graphColoring(graph, color, node + 1, numColors)) { // 递归着色一个节点 return true; } } } return false; // 所有颜色均不可用 } int main() { int n, m; // n个节点,m条边 cin >> n >> m; vector<vector<int>> graph(n, vector<int>(n, 0)); for (int i = 0; i < m; i++) { // 构建邻接矩阵 int u, v; cin >> u >> v; graph[u][v] = graph[v][u] = 1; } int numColors; cin >> numColors; // 可用颜色数 vector<int> color(n, 0); // 初始所有节点颜色为0 if (graphColoring(graph, color, 0, numColors)) { // 从第0个节点开始着色 cout << "Graph is colored with " << numColors << " colors:\n"; for (int i = 0; i < n; i++) { cout << "Node " << i << " is colored with " << color[i] << endl; } } else { cout << "Graph cannot be colored with " << numColors << " colors!" << endl; } return 0; } ``` 这段代码实现了基于回溯法着色,通过输入节点数、边数、邻接矩阵和可用颜色数,输出着色方案。具体实现如下: 1. 定义一个函数`graphColoring`,它将尝试为给定的节点着色,如果着色方案可行,则返回`true`,否则返回`false`。 2. 在`graphColoring`函数中,我们首先检查是否所有节点都已被着色。如果是,则返回`true`,因为所有节点都已被着色。 3. 然后遍历所有颜色,检查当前节点的颜色是否与它的邻接节点相同。如果颜色可用,则着色该节点,然后递归着色一个节点。 4. 如果递归调用返回`true`,则表示找到了一个可行方案,返回`true`。否则,继续遍历其他颜色。 5. 如果所有颜色都不可用,则返回`false`,表示无法找到可行的着色方案。 6. 在主函数中,我们读取输入数据,调用`graphColoring`函数进行着色,并输出结果。 希望这个实现对您有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值