藍橋杯_ 歷屆試題 分考場 (圖的著色問題)
阿新 • • 發佈:2018-12-24
問題描述:存在一個無向圖,要求給圖中的點塗色,並且有線連線的點之間不能是同一種顏色。
問題思路:回溯
#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; }