題目:二分圖匹配
二分圖匹配,就是解決 一羣人,喜歡一類東西,而後求最多知足能知足多少人的問題。固然,東西是不一樣的,人的喜愛也是不一樣的。node
這裏講一下匈牙利算法是如何解決這個問題的。
首先,咱們畫了一個圖:
而後,咱們對第一我的匹配,也就是找第一我的要的東西,而後就獲得了下圖(粉色表明匹配成功,也就是左邊的人獲得了右邊的東西。草綠色表明遍歷過但未成功)
而後,咱們跳過一大堆已知的操做,獲得下圖:
這時,咱們發現,3號人也想要二號物品,可是已經被1號人拿走了。這改怎麼辦辦呢?咱們發現,1號還能拿3號物品。因而,人1把物2給了人3,拿了物3。
而後, 匹配4號。這時……人2佔了物1,但人2不能拿別的東西了。因而乎,人2拒絕妥協,人4被人2暴打了一頓而人4匹配不了其餘的,由於他對物品1情有獨鍾,不喜歡別的東西,so,他莫得東西了QwQ。c++
如今,全部的人都匹配完了,能拿到東西的最多有三我的,4號被拋棄了,還被暴打了一頓,最大匹配數也就是3。算法
那麼,具體思路講完了,改怎麼實現呢?
很簡單,咱們逐個枚舉人,\(dfs(i)=1\)表示匹配成功,不然失敗。用\(1\)個\(chos\)數組,\(chos_i\)表示第\(i\)個物品被第\(chos_i\)我的拿走了,\(vis\)數組用來在判斷一我的可否拿別的物品時用的。也就是作個標記,若是不作這個標記,那再判斷這我的可否拿別的物品時他也許還會選擇這個物品。(因此,每次\(dfs\)都要清空\(vis\))\(dfs\)內部就是枚舉他喜歡的每個東西,若是這個東西沒人要或要這個東西的人能夠拿別的,這個東西就歸他了,而後返回1。若是遍歷完全部他想要的還得不到任何東西,就返回0。
\(vis\)數組要在每一次遍歷的時候清空哦數組
上代碼:spa
#include<bits/stdc++.h> using namespace std; int n,m,e,ans;//e是邊數,ans是答案 bool vis[100005]; int chos[100005]; struct node { int tot; int dt[10000005],nxt[10000005]; int hd[100005]; void add(int x,int y) { tot++; nxt[tot]=hd[x]; hd[x]=tot; dt[tot]=y; return ; } }g;//鏈式前向星存圖 bool dfs(int x) { for(int i=g.hd[x];i;i=g.nxt[i])//枚舉他喜歡的每個東西 { if(vis[g.dt[i]]) continue;//被判斷過,就直接下一個循環 vis[g.dt[i]]=1;//標記,若是不標記,後面的判斷dfs(chos[g.dt[i]])就永遠是真了。 if(!chos[g.dt[i]]||dfs(chos[g.dt[i]]))//若是當前沒人要這個東西或要這個東西的人還能夠拿別的,那這個東西就是他的了 { chos[g.dt[i]]=x;//標記這個東西歸他了 return 1;//返回true。 } } return 0;//到遍歷完還沒獲得東西,返回false } int main() { scanf("%d%d%d",&n,&m,&e); for(int i=1;i<=e;i++) { int u,v; scanf("%d%d",&u,&v); if(u>n||v>m) continue;//題目中說有可能會出現u>n和v>m的狀況 g.add(u,v);//連邊 } for(int i=1;i<=n;i++)//枚舉每一個人 { memset(vis,0,sizeof(vis));//清空vis ans+=dfs(i);//直接加就能夠了,由於bool中true的值是1,false的值是0 } printf("%d",ans); return 0; }