用於求二分圖匹配的最佳匹配。何為最佳匹配?就是帶權二分圖的權值最大的完備匹配稱為最佳匹配。 那麼何為完備匹配?x部中的每乙個頂點都與y部中的乙個頂點匹配,或者y部中的每乙個頂點也與x部中的乙個頂點匹配,則該匹配為完備匹配。
二分圖:就是乙個圖中的頂點集v可分割為兩個互不相交的子集,並且圖中每條邊依附的兩個頂點都分屬於這兩個互不相交的子集(如x、y),兩個子集內的頂點不相鄰。
這裡我會一步一步的說明的,相關的名詞也會慢慢介紹,對於增廣路這個概念,我個人覺得在**中沒體現出來,所以就沒打算說這個事,如果使用增廣路,然後取反得到完備匹配的話,那麼個人感覺是需要設定兩種狀態,一種是可匹配(未匹配),另一種是已匹配,然後取反操作,似乎這樣就變得有些難理解了,所以在這裡並不打算說這個。
演算法步驟如下:
1.用鄰接矩陣(或其他方法也行啦)來儲存圖,注意:如果只是想求最大權值匹配而不要求是完全匹配的話,請把各個不相連的邊的權值設定為0。
2.運用貪心演算法初始化標桿。
3.運用匈牙利演算法找到完備匹配(遍歷x集合的每個頂點)。
4.如果找不到,則通過修改標桿,增加一些邊。
5.重複3,4的步驟,直到完全匹配時可結束。
在這裡設cx[i]為x集合的標桿,cy[i]為y集合的標桿,w[i][j]為圖的鄰接矩陣表示x[i]–y[j]邊的權值。
標桿:是為了求最大(小)權值而引入的乙個概念,用於儲存x集合每個頂點到y頂點中的最大值,而y集合的標桿值全初始化為0,如下圖中,x集合為,而y集合為,每個x的頂點標桿值為路徑權值的最大值,而y的頂點的標桿值初始化為0.
重點:因為使用了標桿這個概念,我們在使用匈牙利演算法進行邊匹配的時候就不能是看邊是否存在,也就是判斷w[i][j]==0
來判斷邊是否存在,引入標桿後,我們判斷邊是否存在(這裡指的是匹配的兩個標桿的值的合是否與路徑值相等,不相等就不存在),即cx[i]+cy[j]-w[i][j]==0
,如果該等式成立的話,就表明這兩個頂點可以匹配(連線),但並不表明這兩個頂點可以直接匹配(連線),因為可能還有其他點與其連線,所以在這種條件下,存在兩種情況:
1、如果這個成立且y[j]邊沒有被x集合中其他頂點匹配。
2、y[j]邊已經被x集合中的某一頂點x[k]匹配,但是x[k]可以匹配其他的頂點,或者是x[i]可以改選其他的匹配。
只要滿足以上兩個條件之一,x[i]與y[j]就可以匹配,然後這裡問題又來了,如果碰上情況2的話,如何讓x[k]去匹配其他頂點呢?這時候,我們就要靠d值去更新標桿值,d=min(cx[i]+cy[j]-w[i][j])
,這時候標桿的作用就體現出來了,不太了解是什麼意思的話,我這裡舉乙個例子:
在上圖中,首先a與d匹配,記作a-d,然後b的標桿值為14,而14+0=14,所以b也要去匹配d,此時就碰上條件2的問題,那麼我們該讓a、b兩點誰去匹配其他點呢?,這時候就得根據d值進行選擇,從圖上,我們可以看出d=cx[a]+cy[e]-w[a]][e]=3
,是a到除了被匹配的點以外的其他點(即除了d點以外的點)權值變化最小的d值,而b到除了d點以外的點的變化最小的d值為cx[b]+cy[f]-w[b][f]=6
,這裡我們就可以看出,如果要改動b的話,需要變化(或者說是變動)的權值為6,相比起改動a的匹配點僅僅需要變化3來說,改動b是不划算(對於求最大權值來說是不划算),所以我們就更新各個標桿的值,讓a可以去匹配e,即a-e,b-d,當然,這裡並沒完成匹配,只是更新標桿值。
到這裡,應該清楚如何新增新的邊了,新增完新的邊後,對應的標桿值也需要更新,即a、b的標桿值-d,d的標桿值+d,這裡我們就需要清楚,到底是哪些值會受影響,如上面受影響的是集合x中的與y集合中的,也就是當我們遍歷b點匹配的時候,匹配形成的路線是b-d-a,是一條不合法(衝突)的路徑,此時設定乙個集合s儲存不合法路徑中的x集合元素a、b,集合t儲存不合法路徑中的y集合元素d,這樣每次更新d值的時候,遍歷s集合進行-d,遍歷t集合進行+d即可。
上面算是對步驟3、4進行詳解了,接下來可以來手工模擬上圖的求值步驟。
1、遍歷x集合,首先,a可匹配d,d沒有被匹配過,a匹配d。
2、b可匹配d,而a已經匹配d,形成b-d-a不合法路徑,此時s集合為,t集合為,那麼從a、b兩者中找匹配e、f變動權值最小的點進行更改匹配,經過d值比較得出a匹配e變動較小,d=3,所以更新s集合與t集合中的標桿值,值的變化使得a可以匹配e,也仍舊可以匹配d。
3、更新完值後清空集合s、t,再次遍歷b點匹配,然後b與d匹配,a與e匹配。
4、c無可匹配的點,此時s集合為,t集合為{},那麼從c匹配d、e、f中找d值變動最小的進行匹配,此時d標桿值為3,所以d1=cx[c]+cy[d]-w[c][d]=3,d2=cx[c]+cy[e]-w[c][e]=1,d3=3,選取最小的d2=1,然後更新集合s中元素的值,此時s集合只有c,所以c標桿值=cx[c]-d=12。
5、因為c沒有匹配到值,所以還得繼續匹配,此時c可以匹配e,而a已經匹配e了,形成c-e-a-d-b不合法路徑,此時s集合為,t集合為,這時候找a、b、c到點f變動最小得值d,然後再更新s集合與t集合各頂點的標桿,在這裡d=2,即c可匹配f變動最小。
6、更新完值後清空集合s、t,c標桿值為10,再次為c進行匹配,此時f可匹配,因為x集合點遍歷完成,所以程式結束。
7、經過上面的匹配,得到的結果是a-e,b-d,c-f,則最大值就是12+14+10=36.
下面是偽**,和實際**其實差別不大…
findpath函式,即找point到y集合中每個點是否存在匹配路徑,s、t則負責記錄經過的路徑用於在找不到匹配路徑的情況下新增新的邊,以及防止進入死遞迴。
//鄰接矩陣的行與列值
int row,col;
//記錄x-y頂點匹配的陣列,即link[y]=x,表示集合y的第y個頂點與x集合的第x頂點匹配
int link[maxline]=;
//儲存圖的權值的鄰接矩陣
int w[maxline][maxline]=;
//儲存x、y標桿權值的陣列
int cx[maxline]=,cy[maxline]=;
bool findpath(int point,char *s,char *t)
} return false;
}int km()
實際**採用的是別人寫的,出自於:二分圖(三)——km演算法
個人僅僅修改了部分**以及注釋,因為邏輯挺清楚的,就不打算重寫了,這裡也挺佩服這些演算法競賽的人,初中高中就在學這些演算法,想想我那時候還在網咖玩遊戲…哈哈…
#include#include#includeusing namespace std;
const size_t size = 1000;
int w[size][size];
const int inf = (1 << 20) - 1;
int m, n;
int cx[size], cy[size];
bool u***[size], usey[size];//用於記錄每輪的集合s與t
int link[size];//link[i]=x代表集合y[i]中的頂點與x匹配或者連線
//dfs 1、return false,用於記錄集合s與t,通過深度遍歷,找到連線路徑上的點。
//2、retrurn true,用於記錄匹配,或者對已匹配的點進行再匹配。
bool dfs(int u)
} return 0;
}int km()
}if (d == inf)
return -1;
//更新s、t集合中的標桿值。
for (int i = 1; i <= n; i++)
if (u***[i])cx[i] -= d;
for (int i = 1; i <= m; i++)
if (usey[i])cy[i] += d;
} }int ans = 0;
for (int i = 1; i <= m; i++)
return ans;
}int main()
return 0;
}
使用 Profile解決不同環境下配置
spring 3.1開始引入 profile註解,此註解可以配合不同環境的profile使用,可以實現靈活配置。比如某些配置只能指定環境啟用,就可以使用此註解。spring會根據不同profile才會對使用相同環境的配置類加入spring的容器進行管理。比如某配置類a上加了 profile dev ...
ABAP FPM list 同一列元件不同行下拉值
利用get data 入參io extended ctrl io extended ctrl type ref to if fpm list ats ext ctrl optional provides extended list ats specific configuration options...
解決不同瀏覽器相容問題心得
1.針對不同瀏覽器的hack寫法 firefox moz document url prefix webkit枘核瀏覽器 chrome and safari media screen and webkit min device pixel ratio 0 ie 請轉2.css reset css的樣...