上次咱們過了二分圖的最佳匹配,如今咱們看一道題目,經典的二分圖的最佳匹配題目算法
時間限制:500MS 內存限制:1000K
提交次數:71 經過次數:24編程
題型: 編程題 語言: G++;GCC數組
SCAU車隊要去參加汽車拉力比賽啦。拉力比賽中,每輛汽車須要一個駕駛員和一個導航員。SCAU車隊一共有有N個駕駛員和N個導航員,每一個 駕駛員對每一個導航員有一個默契值,如今須要求得某種N對N的配對,使得這N個配對的默契值之和最大。
ide
1 5 2 10 20 25 5 0
5 45 至於如何作,我就不說了,看看我之前的博客吧,思路應該很清晰的
#include <stdio.h> #include <stdlib.h> #include <string.h> #define inf 99999 //保存一個咱們認爲的無窮大 int n; int match[50];//表示這個[駕駛員]搭配那個駕駛員 int e[50][50];//用來保存一張圖,表示i-->j的默契值 int vx[50];//用來表示 駕駛員 來找過partner int vy[50];//用來表示 導航員 被邀請過搭配 int fx[50];//一開始是用來表示每個駕駛員與1--n個導航員的max,可是隨着程序執行,會變化哦 int fy[50];//一開始是0的。咱們稱這兩個東西爲 標幹 int dfs (int u)//我要來找u的partner { vx[u]=1;//u已經來找過partner啦,標記一下先,待會的km算法須要 int i; for (i=1;i<=n;i++)//篩選n個 導航員 能夠知道,駕駛員比較「主動」 { if (vy[i]==0&&fx[u]+fy[i]==e[u][i])//這個導航員沒有被邀請過..導航員爲u //駕駛員和導航員的【標槓】值相加,有這個默契值的話,這樣選出來的是最優的 { //vy[i]==1;//這個導航員被邀請過了,可是,接不接受?仍是另一回事 //不是等於等於 //由於他還多是被別人搭配了嘛 vy[i]=1; if (match[i]==0||dfs(match[i]))//若是他沒被人搭配了或者,它能夠找其餘人搭配 { match[i]=u;//i搭配u 改變條件的 //意思就是導航員i和駕駛員u搭配 return 1;//搭配成功 } } } return 0;//我找不到啊,後面,就會執行km } void do_km()//km算法 //對每個在vx[]數組中的駕駛員,咱們對他進行操做 //操做對象是每個不在vy[]數組中的導航員,在了的導航員是不能動的,是當前的最優解 { int i,j; int d;//表示默契值減少得最多,就是,搭配最大 d=inf;//認爲他是無限大 for (i=1;i<=n;i++)//遍歷每個駕駛員 { if (vx[i]==1)//表示他是遍歷過的 { for (j=1;j<=n;j++)//對他進行遍歷導航員 { if (!vy[j])//沒有vy過的導航員才操做 { if (d>(fx[i]+fy[j]-e[i][j]))//對於後面沒vy的導航員,選出最小差值 { d=fx[i]+fy[j]-e[i][j]; //printf ("%d\n",d); } } } } } //對於每個vx[]的 fx[] 減去d //對於每個vy[]的 fy[] 加上d //這樣,總值會減少哦,就是,咱們有機會加入新的導航員,留下空位 for (i=1;i<=n;i++) { if (vx[i]==1) { fx[i] -= d; vx[i]=0;//請0 } if (vy[i]==1)//是訪問過的哦 { fy[i] += d; vy[i]=0;//情0 } } return ; } void work () { int i,j; memset (e,0,sizeof(e)); memset (vx,0,sizeof(vx)); memset (vy,0,sizeof(vy)); memset (fx,0,sizeof(fx)); memset (fy,0,sizeof(fy)); memset (match,0,sizeof(match)); for (i=1;i<=n;i++) { for (j=1;j<=n;j++) { scanf ("%d",&e[i][j]);//不是無向圖哦,有向的 //就是e[i][j]!=e[j][i] 有可能相等 } } //km算法的一部分,先初始化fx,fy for (i=1;i<=n;i++)//遍歷每個駕駛員 { fy[i]=0; fx[i]= -inf;//無窮小 for (j=1;j<=n;j++)//遍歷每個導航員 { if (fx[i]<e[i][j])//默契值 { fx[i]=e[i][j]; } } } for (i=1;i<=n;i++)//遍歷每個駕駛員 { memset (vx,0,sizeof(vx));//每個駕駛員都是新的開始 memset (vy,0,sizeof(vy));//要清零, while (!dfs(i))//若是他找不到搭配,就實現km算法 { do_km();//km完後,仍是會對這個想插入的節點進行dfs的,由於他還沒搭配成功嘛 } } //進行計數 //如今match[i]記錄的是[導航員i]和[駕駛員match[i]]搭配 int ans=0; for (i=1;i<=n;i++)//遍歷導航員 { ans += e[match[i]][i];//輸入的第一個是駕駛員,第二個是導航員 } printf ("%d\n",ans); return ; } int main () { while (scanf ("%d",&n)!=EOF && n) { work (); } return 0; }