图着色问题

一、图着色问题

(1)图的m可着色判定问题

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

(2)图的m可着色优化问题

若一个图最少需要m种颜色才能使图中每条边连接的2个顶点着不同颜色,则称这个数m为该图的色数。


二、m可着色判定问题的解法

【算法】

(1)通过回溯的方法,不断的为每一个节点着色,在前面cur-1个节点都合法的着色之后,开始对第cur-1个节点进行着色,

(2)这时候枚举可用的m个颜色,通过和第cur-1个节点相邻的节点的颜色,来判断这个颜色是否合法

(3)如果找到那么一种颜色使得第cur-1个节点能够着色,那么说明m种颜色的方案在当前是可行的。
(4)cur每次迭代加1,如果cur增加到N并通过了检测,说明m种颜色是可满足的。

(5)注意,这里只是要求判断m种颜色是否可满足,所以找到任何一种方案就可以了。

【代码实现】

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 105;
int G[maxn][maxn];
int color[maxn];
bool ans;
int n,m,k;
void init(){
    ans = 0;
    memset(G, 0 , sizeof G);
    memset(color, 0 , sizeof color);
}

void dfs(int cur){
    if(cur > n) {
        ans = 1;
        return;    
    } 
    for(int i=1; i<=m; i++){ //对cur结点尝试使用每一种颜色进行涂色 
    
        bool flag = 1;
        //cur之前的结点必被涂色
        for(int j=1; j<cur; j++){
            if(G[j][cur] == 1 && color[j] == i){
                flag = 0;//只要有一个冲突都不行 
                break;
            }
        } 
        //如果可以涂上i颜色,则考虑下一个结点的情况 
        if(flag){
            color[cur] = i;
            dfs(cur + 1);
            
        } 
        //如果到这一步第cur个结点无法着色,则返回探寻其他方案 
        else color[cur] = 0;//回溯 ;
    }
    
}


int main(){
    while(cin>>n>>k>>m){
        init(); 
        for(int i=1; i<=k; i++){
            int x,y;
            cin>>x>>y;
            G[x][y] = G[y][x] = 1;
        }
        dfs(1);
        cout<<ans<<endl;
    }
    return 0;
}

 

 

三、m可着色拓展

【问题】在上述基础上,求出m种颜色能够给图G涂色的总总方案数量

【算法】由于这个时候要求总方案数量,所以在找到一种可行方案后,总是进行回溯再搜索其他的解决方案,与上面不同,上面是只需要找出一种方案即可,所以如果找到了就不需要再回溯了,所以在这里只需要把回溯语句的位置写到dfs语句的后面即可。

【代码实现】

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 105;
int G[maxn][maxn];
int color[maxn];
int ans;
int n,m,k;
void init(){
    ans = 0;
    memset(G, 0 , sizeof G);
    memset(color, 0 , sizeof color);
}

void dfs(int cur){
    if(cur > n) {
        ans++;
        return;    
    } 
    for(int i=1; i<=m; i++){ //对cur结点尝试使用每一种颜色进行涂色 
    
        bool flag = 1;
        //cur之前的结点必被涂色
        for(int j=1; j<cur; j++){
            if(G[j][cur] == 1 && color[j] == i){
                flag = 0;//只要有一个冲突都不行 
                break;
            }
        } 
        //如果可以涂上i颜色,则考虑下一个结点的情况 
        if(flag){
            color[cur] = i;
            dfs(cur + 1);
            color[cur] = 0;//回溯 
        } 
    }
    
}


int main(){
    while(cin>>n>>k>>m){
        init(); 
        for(int i=1; i<=k; i++){
            int x,y;
            cin>>x>>y;
            G[x][y] = G[y][x] = 1;
        }
        dfs(1);
        cout<<ans<<endl;
    }
    return 0;
}

 

四、图的m可着色优化问题

【问题】给定无向图图G,顶点数N,边集E, 要求用最少的颜色使得图中每一个顶点都涂上颜色,要求有边直接相连的顶点不能涂同样的颜色,问最少需要多少种颜色?

【算法】

(1)一种朴素的想法就是在上述图着色的结论基础上,使用二分法求出所需要的最小颜色数,范围1~N。但是存在大量的重复计算,复杂度过高。

(2)可以在之前的DFS上加上一维颜色数,一边增加顶点数一边增加颜色数,留下能够使得1~N顶点都满足的最少的颜色数

【题目链接】分考场

题目大意是:有n个学生分考场,其中有k对学生认识,认识的学生不能在同一个考场,问最少需要多少个考场。

相当于有n个顶点,k条边,有边相连的顶点不能涂相同的颜色,问最少需要多少颜色。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 105;
const int inf = 0x3f3f3f3f;
int n,k;
int x,y;
int room[maxn];//room[i]表示i号房间的当前人数
int stu[maxn][maxn];//stu[i][j]表示第i个房间的第 j个学生是谁
int G[maxn][maxn];//存图
int ans ; 
void init(){
    memset(room ,  0, sizeof room);
    memset(stu, 0,  sizeof stu);
    memset(G,  0, sizeof G);
    ans = inf;
}

//表示给第cur个学生安排房间 
void dfs(int cur , int tot){
    if(tot >= ans) return ;
    //如果学生都安排好了,则记录这种方法所导出的ans 
    if(cur > n){
        ans = min(ans , tot);
        return ;
    } 
    //如果还没安排好,则给cur找合适的房间 
    for(int i=1; i<=tot; i++){
        //看第i号房间里的所有人是否与其冲突
        int len = room[i];
        bool flag = 1;
        for(int j=1; j<=len; j++){
            if(G[stu[i][j]][cur]){
                flag = 0;
                break;
            }
        }
        //如果找到房间了 就去安排下一个学生 
        if(flag){
            room[i]++;//这个房间里多住了cur
            stu[i][room[i]] = cur;
            //继续深搜 
            dfs(cur+1, tot); 
            //回溯
            stu[i][room[i]] = 0;
            room[i]--;
            
        } 
    }
    //如果这 tot个房间都不行,则需要新开间房给这个学生
    room[tot+1]++;
    stu[tot+1][room[tot+1]] = cur;
    //继续深搜 
    dfs(cur + 1 , tot + 1); 
    //回溯 
    stu[tot+1][room[tot+1]] = 0;
    room[tot+1]--;
}  


int main(){
    while(cin>>n>>k){
        init();
        for(int i=1; i<=k; i++){
            cin>>x>>y;
            G[x][y] = G[y][x] = 1;
        }
        dfs(1,0);
        cout<<ans<<endl;
    }
}

 

posted @ 2019-03-19 15:01  西风show码  阅读(6927)  评论(0编辑  收藏  举报