1. 程式人生 > >藍橋杯_ 歷屆試題 分考場 (圖的著色問題)

藍橋杯_ 歷屆試題 分考場 (圖的著色問題)

        問題描述:存在一個無向圖,要求給圖中的點塗色,並且有線連線的點之間不能是同一種顏色。

        問題思路:回溯

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <vector>
#include <queue>
#define ll long long
#define INF 0x3f3f3f3f

using namespace std;
const int MAXN = 110;
int graph[MAXN][MAXN];
int color[MAXN];
int n,m,num_col;

bool Judge(int x,int col)//判斷當前情況下,這個點能比能是col這個顏色
{
    for(int i = 1;i <= n;i ++)
    {
        if(graph[i][x] && color[i] != 0 && color[i] == col )
            return false;
    }
    return true;
}
void Init()
{
    scanf("%d%d",&n,&m);//n邊的條數,m點的個數
    int a,b;
    scanf("%d",&num_col);//能用的顏色數目
    memset(graph,0,sizeof(graph));//存圖
    memset(color,0,sizeof(color));//每個點的顏色
    for(int j =0 ;j < n;j ++)
    {
        scanf("%d%d",&a,&b);
        graph[a][b] = graph[b][a] = 1;
    }
}
void Solve(int pos)
{
    if(pos == n)
    {
        for(int i =1 ;i <= m;i ++)
            printf("%d%c",color[i],i==m?'\n':' ');
        return ;
    }
    for(int i = 1;i <= num_col;i ++)
    {
        if(Judge(pos,i))
        {
            color[pos] = i;
            Solve(pos+1);
            color[pos] = 0;
        }
    }
}
int main()
{
    Init();
    Solve(1);
}

但是我們還有一個問題,就是如果給你了圖的邊和點,我們應該計算所需要最少的顏色呢?

我們有一個例題:

問題描述  n個人參加某項特殊考試。
  為了公平,要求任何兩個認識的人不能分在同一個考場。
  求是少需要分幾個考場才能滿足條件。輸入格式  第一行,一個整數n(1<n<100),表示參加考試的人數。
  第二行,一個整數m,表示接下來有m行資料
  以下m行每行的格式為:兩個整數a,b,用空格分開 (1<=a,b<=n) 表示第a個人與第b個人認識。輸出格式  一行一個整數,表示最少分幾個考場。樣例輸入5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5樣例輸出4樣例輸入5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5樣例輸出5

因為這個問題的資料量很小,所以我們想到了回溯,在已知的狀態下,然後去判斷下一個學生是否可以在之前存在的教室,如果不可以的話,那麼就新開一個教室。

因為這個題的時間限制,沒有使用vector,所以程式理解起來可能會有一點麻煩。

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>

using namespace std;

const int maxn = 110;
const int inf = 0x3f3f3f3f;
int gra[maxn][maxn];   //是否存在關係,存在關係就是1,不存在關係就是0
int cun[maxn][maxn];   //第一維度表示的是考場,二位度裡面放的是這個考場裡面的學生
//cun[1][1] = 2,cun[1][2] = 0;表示考場1裡面第一個存在的人是2,然後後面一位是0,也就是不存在人了,那麼這時的cun[1] = 1;表示的是考場裡面人的數量
int cnt[maxn];         //是cun陣列存的是學生的數量
int res=inf;
int n,m;
void solve(int id,int num){    //id表示學生,num表示當前考場的編號
    if(num>=res){              //當現在安排的數量已經大於了最小的教室數量的話,返回
        return;
    }
    if(id>n){                 //安排的學生已經大於所有的學生了,就是安排完所有的學生了已經
        res=min(res,num);
        return;
    }
    for(int i=1;i<=num;i++){   //首先看看之前的房間裡面能不能放進去
        int sz=cnt[i];
        int jishu=0;      //得到的是和這個人不認識的人數
        for(int j=1;j<=sz;j++){
            if(gra[id][cun[i][j]]==0){
                jishu++;
            }
        }
        if(jishu==sz){   //如果這裡面的學生都和現在遍歷的都不認輸
            cun[i][++cnt[i]]=id; //將這個學生存到這個考場中
            solve(id+1,num);
            cnt[i]--;
        }
    }
    //重新開一個房間
    cun[num+1][++cnt[num+1]]=id;  //沒有的話就把它放到下一個教室裡
    solve(id+1,num+1);
    --cnt[num+1];
}
int main(){
    scanf("%d%d",&n,&m);
    memset(gra,0,sizeof(gra));
    memset(cnt,0,sizeof(cnt));
    while(m--){
        int a,b;
        scanf("%d%d",&a,&b);
        gra[a][b]=gra[b][a]=1;
    }
    solve(1,0);
    printf("%d\n",res);
    return 0;
}