hdu2255 奔小康賺大錢,最大權匹配,KM算法
點擊打開鏈接
最大權匹配
KM算法
算法步驟:
設頂點Xi的頂標為a[i],頂點Yi的頂標為b[i]
ⅰ.初始時。a[i]為與Xi相關聯的邊的最大權值。b[j]=0。保證a[i]+b[j]>=w(i,j)成立
ⅱ.當相等子圖中不包括完備匹配時,就適當改動頂標以擴大相等子圖,直到找到完備匹配為止
ⅲ.改動頂標的方法
當從Xi尋找交錯路失敗後,得到一棵交錯樹,它的全部葉子節點都是X節點。對交錯樹中X頂點的頂標降低d值,Y頂點的頂標添加d值,對於圖中全部的邊(i,j),
能夠看到:
i和j都不在交錯樹中,邊(i,j)仍然不屬於相等子圖
i和j都在交錯樹中,邊(i,j)仍然屬於相等子圖
i不在交錯樹中。j在交錯樹中,a[i]+b[j]擴大。邊(i,j)不屬於相等子圖
i在交錯樹,j不在交錯樹中,邊(i,j)有可能增加到相等子圖中
為了使a[i]+b[j]>=w(i,j)始終成立,且至少有一條邊增加到相等子圖中,d=min{a[i]+b[j]-w(i,j)},i在交錯樹中,j不在交錯樹中
時間復雜度:須要找O(n)次增廣路。每次增廣最多須要改動O(n)次頂標。每次改動頂標時枚舉邊來求d值,復雜度為O(n2),總的復雜度為O(n4).簡單優化能夠減少到O(n3),每一個Y頂點一個“松弛量”函數slack,每次開始找增廣路時初始化為無窮大。
在尋找增廣路的過程中,檢查邊(i,j)時。假設不在相等子圖中,則讓slack[j]變成原值與A[i]+B[j]-w[i,j]的較小值。這樣。在改動頂標時,取全部不在交錯樹中的Y頂點的slack值中的最小值作為d值就可以。但還要註意一點:改動頂標後,要把全部的slack值都減去d。
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <set> #include <map> #include <stack> #include <queue> #include <vector> #include <string> #define for0(a,b) for(a=0;a<b;++a) #define for1(a,b) for(a=1;a<=b;++a) #define foru(i,a,b) for(i=a;i<=b;++i) #define ford(i,a,b) for(i=a;i>=b;--i) using namespace std; typedef long long ll; const int maxn = 310; const int INF = 1e9; /*KM算法 *O(nx*nx*ny) *求最大權匹配 *若求最小權匹配,可將權值取相反數,結果再取相反數。 */ int nx, ny; int g[maxn][maxn]; int linker[maxn], lx[maxn], ly[maxn];//y中各點匹配狀態,x,y中的頂標 int slack[maxn]; bool visx[maxn], visy[maxn]; bool DFS(int x) { visx[x] = true; for(int y=0; y<ny; ++y){ if(visy[y]) continue; int tmp = lx[x] + ly[y] - g[x][y]; if(tmp == 0){ visy[y] = true; if(linker[y] == -1 || DFS(linker[y])){ linker[y] = x; return true; } } else if(slack[y]> tmp) slack[y] = tmp; } return false; } int KM() { memset(linker, -1, sizeof linker ); memset(ly, 0, sizeof ly ); for(int i=0; i<nx; ++i){ lx[i] = - INF; for(int j=0; j<ny; ++j) if(g[i][j]> lx[i]) lx[i] = g[i][j]; } for(int x=0; x<nx; ++x) { for(int i=0; i<ny; ++i) slack[i] = INF; while(true) { memset(visx, false, sizeof visx ); memset(visy, false, sizeof visy ); if(DFS(x)) break; int d = INF; for(int i=0; i<ny; ++i) if(!visy[i] && d>slack[i]) d = slack[i]; for(int i=0; i<nx; ++i) if(visx[i]) lx[i] -= d; for(int i=0; i<ny; ++i) { if(visy[i]) ly[i] += d; else slack[i] -= d; } } } int res = 0; for(int i=0; i<ny; ++i) if(linker[i] != -1) res += g[linker[i]][i]; return res; } //HDU 2255 int main() { #ifndef ONLINE_JUDGE freopen("in.cpp","r",stdin); freopen("out.cpp", "w", stdout); #endif // ONLINE_JUDGE int n; while(~scanf("%d", &n)) { for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) scanf("%d", &g[i][j]); nx = ny = n; printf("%d\n" ,KM()); } return 0; }
hdu2255 奔小康賺大錢,最大權匹配,KM算法