1. 程式人生 > >二分圖染色&【洛谷習題】封鎖陽光大學

二分圖染色&【洛谷習題】封鎖陽光大學

特殊 true 輸出 sed pty digi 劃分 += pen

二分圖是一種特殊的圖論模型,什麽是二分圖呢?我們知道圖是由點集和邊集構成的,如果可以把圖的點集分成兩部分,而圖中的每條邊都是一個端點屬於其中一個集合,另一個端點屬於另一個集合,我們把這樣的圖稱為二分圖(嚴謹定義請自行百度)。二分圖有什麽性質呢?二分圖可以做到,給每個結點染色(只染成兩種顏色),不會出現相鄰的結點顏色相同的情況。因為二分圖可以被劃分成上述的兩個集合,給其中一個集合染上一種顏色,給另一個集合染上另一種顏色,那麽每條邊的兩端都是不同顏色的點,因此不會出現相鄰結點顏色相同的情況。同樣的,二分圖染色其實就是把圖劃分成這麽兩個集合的過程。

技術分享圖片

給一個圖進行二分圖染色的過程也可以順便判斷一個圖是不是二分圖,這是因為二分圖一定可以進行二分圖染色,可以進行二分圖染色的一定是二分圖。那麽如果我們在染色過程中遇到沖突的話(兩個相鄰結點顏色相同),就說明這不是一個二分圖。二分圖染色類似於圖的遍歷,可以使用DFS或BFS,這裏呢,我用的是BFS。對每個結點進行染色,盡量染成與相鄰結點不同的顏色,但如果存在沖突,則不是二分圖,否則可以一直染色下去,將點劃分成兩個集合。

附一道模板題:https://www.luogu.org/problemnew/show/P1330


做這道題時,我還不會二分圖染色,就用了貪心,每次刪除度最大的點,只有40分。學了二分圖染色後,發現這就是個簡單的模板題。因為要保證每個放河蟹的點不能相鄰,其實就是一個二分圖染色問題,不過是需要統計染上兩種顏色的點中,最少的那一類點。另外,圖不保證連通,對於每個連通塊都要進行一次染色,分別統計答案。

技術分享圖片
 1 #include<cstdio>
 2 #include<cctype>
 3 #include<cstring>
 4 #include<queue>
 5
using namespace std; 6 inline int get_num() { //讀入優化 7 int num; 8 char c; 9 while((c=getchar())==\n||c== ||c==\r); 10 num=c-0; 11 while(isdigit(c=getchar())) num=num*10+c-0; 12 return num; 13 } 14 void put_num(int i) { //輸出優化 15 if(i<0) putchar(-),i=-i;
16 if(i>9) put_num(i/10); 17 putchar(i%10+0); 18 } 19 int min(int a,int b) { 20 if(a<b) return a; 21 return b; 22 } 23 const int maxn=1e4+5,maxm=2e5+5; 24 int n,m,head[maxn],eid,color[maxn],cnt[2],ans; //cnt用於統計兩種顏色的點各自的數量 25 struct edge { //使用鄰接表存儲 26 int v,next; 27 edge(int v=0):v(v) {} 28 } E[maxm]; 29 void insert(int u,int v) { 30 E[eid]=edge(v); 31 E[eid].next=head[u]; 32 head[u]=eid++; 33 } 34 queue<int> q; 35 bool bfs(int s) { 36 cnt[0]=cnt[1]=0; //初始化 37 ++cnt[(color[s]=1)-1]; 38 q.push(s); //起點入隊 39 while(!q.empty()) { 40 int u=q.front(); 41 q.pop(); 42 for(int p=head[u];p+1;p=E[p].next) { 43 int v=E[p].v; 44 if(color[v]==color[u]) return false; //沖突則退出 45 if(!color[v]) { //沒染過色就染色,盡量保證不沖突 46 if(color[u]==1) ++cnt[(color[v]=2)-1]; 47 else ++cnt[(color[v]=1)-1]; 48 q.push(v); 49 } 50 } 51 } 52 ans+=min(cnt[0],cnt[1]); //統計每個連通塊答案 53 return true; 54 } 55 int main() { 56 n=get_num(); 57 m=get_num(); 58 int u,v; 59 memset(head,-1,sizeof(head)); //記得初始化 60 for(int i=1;i<=m;++i) { 61 u=get_num(); 62 v=get_num(); 63 insert(u,v); 64 insert(v,u); 65 } 66 for(int i=1;i<=n;++i) { 67 if(color[i]) continue; //已經遍歷過的點不再遍歷 68 if(!bfs(i)) { 69 printf("Impossible"); 70 return 0; 71 } 72 } 73 put_num(ans); 74 return 0; 75 }
AC代碼

二分圖染色&【洛谷習題】封鎖陽光大學