回溯算法 --- 例题7.图的m着色问题


一.问题描述

给定无向连通图G和m种不同的颜色.用这些颜色为图G的各项点着色,每个项点画一种颜色.是否有一种着色法,使G中每条边的2个顶点有着不同颜色?

二.解题思路

图的m色判定问题:
给定无向连通图G和m种颜色。用这些颜色为图G的各顶点着色. 问是否存在着色方法, 使得G中任2邻接点有不同颜色。

图的m色优化问题:
给定无向连通图G,为图G的各顶点着色, 使图中任2邻接点着不同颜色,问最少需要几种颜色。所需的最少颜色的数目m称为该图的色数

若图G是可平面图,则它的色数不超过4色(4色定理).
4色定理的应用:在一个平面或球面上的任何地图能够只用4种颜色来着色使得相邻的国家在地图上着有不同颜色

例如:

[这里有一个适用于任意图着色的Welch Powell法,感兴趣的同学可以看看.](#Welch Powell)

回到该问题,我们可以很清晰地看出问题的解空间树是一棵排列树,因为我们确定n个元素满足某种性质的排列,这个性质就是一条边的两个点颜色不相同.而不是说找到n个元素的一个子集,这是要做的第一步.

具体的算法实现上:
设图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叉树.在解空间树中做深度优先搜索, 约束条件: x[i] ≠ x[j], 如果a[j].[i] = 1.

代码如下:

// 图的m着色问题
#include<bits/stdc++.h>
using namespace std;
class Color
{
    friend int mColoring(int, int, int **);
    private:
        bool CanDraw(int k);
        void Backtrack(int i);
        int n,          //图的顶点数
            m,          //可用颜色数
            **a,        //图的邻接矩阵
            *x;         //当前解
        long sum;       //当前已经找到的可m着色方案数
};
bool Color::CanDraw(int i)      //检查第i层填写的颜色x[i]是否可用
{
    for(int j=1; j<i; j++)
    {
        if(a[i][j]==1 && x[j]==x[i])
            return false;
    }
    return true;
}
void Color::Backtrack(int i)
{
    if(i > n)
    {
        sum++;
        cout<<"第"<<sum<<"个解:";
        for(int i=1; i<=n; i++)
            cout<<x[i]<<" ";
        cout<<endl;
        return;
    }
    for(int k=1; k<=m; k++)  //依次从m种颜色中选择,由于每一个分支的处理方法一样,所以直接用一个循环,而不需要分别写
    {
        x[i] = k;
        if(CanDraw(i))  
        {
            cout<<"颜色"<<k<<"可行,深入一层,将到达"<<i+1<<"层"<<endl;
            Backtrack(i+1);
            cout<<"回溯一层到达第"<<i<<"层"<<endl;
        }
        else 
        {
            if(k==m) cout<<"当前层所有颜色选完,没有可行颜色,故将回溯一层到达第"<<i-1<<"层"<<endl;
            else cout<<"颜色"<<k<<"不可行,继续选择颜色"<<k+1<<endl;
        }
        x[i] = 0;
    }
}
int mColoring(int n, int m, int **a)
{
    Color X;
    // 初始化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;
}
int main()
{
    cout<<"请输入顶点个数和颜色种数:";
    int n, m;
    while(cin>>n>>m && n && m)
    {
        cout<<"请输入邻接矩阵"<<endl;
        int **a = new int*[n+1];
        for(int i=0; i<=n; i++) a[i] = new int[n+1];
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                cin>>a[i][j];
        int ans = mColoring(n, m, a);
        cout<<"图的"<<m<<"着色方案共有"<<ans<<"种"<<endl;
        for(int i=0; i<=n; i++) delete[] a[i];
        delete[] a;
        cout<<"请输入顶点个数和颜色种数:";
    }
    system("pause");
    return 0;
}

运行结果:

由此可以结合排列树看一看,十分清晰明了.

参考毕方明老师《算法设计与分析》课件.

欢迎大家访问个人博客网站---乔治的编程小屋,和我一起加油吧!


 Welch Powell方法 

针对任意图着色问题,Welch Powell方法:

  • 将G的结点按照度数递减的次序排列
  • 用第一种颜色对第一个结点着色,并按照结点排列的次序对与前面着色点不邻接的每一点着以相同颜色
  • 用第二种颜色对尚未着色的点重复步骤2,用第三种颜色继续这种作法,直到所有点着色完为止

例如:


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM