文章首先於微信公衆號:幾何思惟,關注第一時間獲取更新信息算法
如下場景太過真實,但都是虛構,爲了講清楚理論的過程。若有雷同,純屬我瞎編,還望勿對號入座。微信
中國現在男女比例嚴重失衡,2021年預計將有9200萬單身貴族。爲了幫助解決這個社會性問題,提高總體人民的幸福感,小K打算投身到這份偉大的事業中。
「幾何思惟」婚戀所,用最科學的方法,幫你脫單。經過幾率論尋找最佳匹配對象,再經過微積分精確計算好感上升曲線,最後用數值分析無限逼近對方的理想型。最可怕的是,還包郵呢親,關注一波瞭解一下?網絡
上班第一天,老闆給了小K一份單身男女好感的數據資料。以下圖,連線表示雙方互有好感,能夠嘗試處對象。測試
忽然遇到了一個問題,那怎麼才能進行最大的匹配,創造總體人民最大的幸福感呢,固然也能夠順便拿最多的中介費啦。spa
不少時候不是你比別人差,而是你執行力不夠,在猶豫中喪失機會。
你們就先行動起來吧。3d
快看,男1號選手在小K的鼓勵(慫恿)下,率先對女1號發起了進攻。在離失敗只有0.01公分的時候,他居然奇蹟般的完成反殺,沒錯,他成功啦,這種高超的技巧,嫺熟的手法簡直如同教科書通常,值得在座的每一個同窗深刻研究反覆琢磨啊。code
男2號選手也不甘落後,也對女2號選手發起了進攻,沒錯,又一次成功啦。對象
男3號選手:我勒個去,我上我也行啊。因而也對本身心動的女1號發起了進攻,毫無心外,他陣亡了。。。blog
中間彩蛋。遞歸
男3號不甘心,原地復活,想再戰一回。在一個地方跌倒,我們就換一個地方再跌。。。
因而對女2號發起了進攻。
幾經波折。
男3號終於也成爲了有牽絆的男人,不論將來有多久,只在意曾經擁有過。
男4一看:這也沒我啥事兒了啊。
以上的過程其實就是經典的匈牙利算法,求解二分圖的最大匹配問題。
二分圖
定義:設G=(V,E)是一個無向圖,頂點集V可分割爲兩個互不相交的子集X,Y,而且圖中每條邊關聯的兩個頂點都分屬於這兩個互不相交的子集,兩個子集內的頂點不相鄰。
判斷是否爲二分圖的充要條件:G至少有兩個頂點,且其全部迴路的長度均爲偶數。
判斷方法:染色法
可用bfs或者dfs。
匹配
在二分圖G的子圖M中,M的邊集E中的任意兩條邊都不依附於同一個頂點,則稱M是一個匹配。
飽和點
匹配M的邊集所關聯的點爲飽和點,不然爲非飽和點。如上圖:
交錯路
定義:圖G的一條路徑,且路徑中的邊在屬於M和不屬於M中交替出現。
增廣路(非網絡流中的定義)
定義:一條交錯路,且該交錯路的起點和終點都爲匹配M的非飽和點。
如上圖,交錯路1是增廣路;交錯路2不是增廣路,由於終點$$X_1$$不是非飽和點。
由增廣路推出如下結論:
匈牙利算法核心思想:
變量定義及初始化
const int MAXM = 200, MAXN = 200; bool map[MAXN][MAXM] = {false}, visit[MAXM]; int n, m, x[MAXM], y[MAXN], ans = 0;
初始化
void init() { memset(x, 0xff, MAXM * 4); memset(y, 0xff, MAXN * 4); memset(map, false, MAXN * MAXM); int num, temp; cin >> n >> m; for (int i = 0; i < n; ++i) { cin >> num; for (int j = 0; j < num; ++j) { cin >> temp; map[i][temp - 1] = true; } } }
遞歸尋找增廣路
bool hungary(int u) { for (int i = 0; i < m; ++i) { if (!visit[i] && map[u][i]) { visit[i] = true; if (y[i] == -1 || hungary(y[i])) { x[u] = i; y[i] = u; return true; } } } return false; }
遍歷全部點
int main() { init(); for (int i = 0; i < n; ++i) { if (x[i] == -1) { memset(visit, false, MAXM); if (hungary(i)) { ans++; } } } cout << ans << endl; return 0; }
測試數據
輸入 5 5 2 2 5 3 2 3 4 2 1 5 3 1 2 5 1 2 輸出 4
掃描下方二維碼關注公衆號,第一時間獲取更新信息!