2019 年南京 regional 充分暴露了這個問題,市面上大多數標著 o(n
3)
o(n^3)
o(n3
) 的 km 板子實際上是 o(n
4)
o(n^4)
o(n4
) 的,以致選手如果是用了經典書籍上的板子,或者是網上隨便扒的板子,就會 tle。
然後最近做題做到了 km,就想補乙個自己的真·o(n
3)
o(n^3)
o(n3
) 的 km。網上的 o(n
3)
o(n^3)
o(n3
) 也都沒有教程只能自己啃**,就想把思路寫一下。
大二了才會 km 你丟不丟人
乙個簡單粗暴的判斷板子是 o(n
4)
o(n^4)
o(n4
) 還是 o(n
3)
o(n^3)
o(n3
) 的辦法:非遞迴是 o(n
3)
o(n^3)
o(n3
),遞迴(用了匈牙利一樣的東西)是 o(n
4)
o(n^4)
o(n4)。
更加正確的判斷方法:拿去交正確的模板題,比如 uoj#80、2019 南京 regional j(計蒜客復現)。
要解決的是二分圖最大權匹配問題。這是乙個線性規劃問題,它的對偶問題是最小頂標和問題,km 的思路就是計算最小頂標和。
看這位大佬生動的解釋就懂了:
匈牙利是 o(n
2)
o(n^2)
o(n2
) 的,修改頂標也是 o(n
2)
o(n^2)
o(n2
) 的,每個點最多 n
nn 次修改頂標,共 n
nn 個點,因此是 o(n
4)
o(n^4)
o(n4)。
現在是對於左邊的點 i
ii,我們來優化它的匹配過程。按照 o(n
4)
o(n^4)
o(n4
) 的思想,我們是每次匈牙利找增廣路,失敗了就修改頂標。
如果我們預設每一次增廣都會失敗,那麼實際上每一次增廣的目的就成了最小化 d
dd(最小的頂標增量使得相等子圖能新加入一條邊)。我們把這個 d
dd 掛在右邊的結點上,記為 sla
ck
jslack_j
slackj
,表示增廣路最後到達的右邊結點為 j
jj 時,能達到的最小的 ddd。
那麼這就如同 dijkstra,我們從 i
ii 出發,把右邊的結點的 sla
ck
slack
slac
k 全部鬆弛一遍,然後找到 sla
ck
slack
slac
k 最小的 j
jj,sla
ck
jslack_j
slackj
就是本次的頂標增量。(若 sla
ckj=
0slack_j=0
slackj
=0 則頂標不動,相當於匈牙利繼續,若 sla
ck
j>
0slack_j>0
slackj
>
0 相當於匈牙利失敗了要修改頂標。)
然後嘗試匹配 i
ii 和 j
jj。若 j
jj 單身,則牽手成功;若 j
jj 名花有主,則嘗試綠掉原配增廣原配,即從原配出發,把右邊的結點的 sla
ck
slack
slac
k 又鬆弛一遍……
直到最後找到了單身的 j
jj,就增廣成功了。
於是每個 j
jj 只會被嘗試匹配一次,每個原配只會被綠一次增廣一次,這樣就相當於 i
ii 只做了一次如同 bfs 般的多路增廣,所以總時間複雜度為 o(n
3)
o(n^3)
o(n3)。
回頭看我們預設每一次都增廣失敗,實際上如果增廣成功的話就意味著一路 sla
ckj=
0slack_j=0
slackj
=0,這種情況被包括了。
我覺得把它稱為多路增廣,或者 bfs,都不如 dijkstra 貼切。
upd:區分了左邊點數 nlnl
nl和右邊點數 nrnr
nr
ll lx[maxn]
,ly[maxn]
,slack[maxn]
;int f[maxn]
,pre[maxn]
;// f[y]表示右邊的 y 匹配了左邊的誰
bool vis[maxn]
;ll km()
fo(i,
1,nl)
fo(j,
0,nr)
if(vis[j]
) lx[f[j]]-
=d, ly[j]
+=d;
// 修改頂標
else slack[j]
-=d;
// 用於鬆弛它的頂標已經改了,所以它也要改
}for
(; py; py=pre[py]
) f[py]
=f[pre[py]];
// 修改增廣路
} ll re=0;
fo(i,
1,nl) re+
=lx[i];fo
(i,1
,nr) re+
=ly[i]
;return re;
}int
main()
km演算法的非最優匹配應用
km演算法可以用來求最優匹配,但是,它本身蘊含著複雜的數學原理,我暫時還不知道怎麼理解,僅僅在此提一道非匹配應用。題目 爭奪 題目描述 小y和小p無聊的時候就喜歡玩遊戲,但是每次小p都輸給了小y。終於有一天,你看不過去了,決定幫小p一把。遊戲是這樣的,乙個n m的棋盤 保證n或m中,至少有乙個為偶數...
遞迴 遞迴演算法的非遞迴優化
一 遞迴 在方法內部呼叫自身方法的過程稱為遞迴,下面給出乙個遞迴方法的示例。class program 使用遞迴,實現求前n項和 public static int getsum int n int result getsum n 1 在方法體中呼叫方法本身 return result n 需要注意...
樹的非遞迴
code 資料結構.cpp 定義控制台應用程式的入口點。include stdafx.h include using namespace std typedef char elemtype typedef struct lnode lnode,linkstack 棧 typedef struct b...