圖着色問題


一、圖着色問題

(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;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM