1. 程式人生 > >hdu2255 奔小康賺大錢,最大權匹配,KM算法

hdu2255 奔小康賺大錢,最大權匹配,KM算法

rac text long long family tdi return || 擴大 tmp

點擊打開鏈接



最大權匹配

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算法