[HDU2255] 奔小康赚大钱(二分图匹配 | KM算法)

传送门:奔小康赚大钱

裸的 KM 算法,上板子。 

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 3e2+32;
int n, nx, ny;
int g[maxn][maxn];
int linker[maxn], lx[maxn], ly[maxn]; //y点中的匹配状态,xy的杠杆值
int slack[maxn]; //新加边的最小差值
bool visx[maxn], visy[maxn];

void read()
{
    nx = ny = n; 
    for(int i = 0; i < nx; ++i)
        for(int j = 0; j < ny; ++j)
            cin >> g[i][j];
}

bool dfs(int x)
{
    visx[x] = 1;
    for(int i = 0; i < ny; ++i)
    {
        if(visy[i]) //每一轮匹配 右图每个点只访问一次
            continue;
        int t = lx[x] + ly[i] - g[x][i]; //差值
        if(!t)
        {
            visy[i] = 1;
            if(linker[i] == -1 || dfs(linker[i])) //找到一个未匹配的点,形成增广路
            {
                linker[i] = x;
                return 1;
            }
        }
        else
            slack[i] = min(slack[i], t); //更新最小差值
    }
    return 0;
}

int KM()
{
    memset(linker, -1, sizeof(linker)); //无匹配
    memset(ly, 0, sizeof(ly)); //右图各点期望值为0

    for(int i = 0; i < nx; ++i)
    {
        lx[i] = -INF;
        for(int j = 0; j < ny; ++j)
            lx[i] = max(lx[i], g[i][j]); //lx设置为连接的边权的最大值,即期望的最大值
    }

    for(int i = 0; i < nx; ++i)
    {
        memset(slack, INF, sizeof(slack));

        while(1)
        {
            memset(visx, 0, sizeof(visx));
            memset(visy, 0, sizeof(visy));

            if(dfs(i)) //形成了增广路,匹配成功
                break;

            //否则,新加边使得能够匹配
            int d = INF; //计算d值,d为当前加上一条边,左图需要减少的最小的期望值
            for(int j = 0; j < ny; ++j)
                if(!visy[j])
                    d = min(d, slack[j]); //求得最小值
            for(int j = 0; j < nx; ++j)
                if(visx[j]) //在访问过的点中求
                    lx[j] -= d; //左图减去最小的期望差值
            for(int j = 0; j < ny; ++j)
            {
                if(visy[j]) //在访问过的点中求
                    ly[j] += d; //右图加上最小的期望差值
                else
                    slack[j] -= d; //左图期望降低了,那么差值就变小了
            }
        }
    }
    int ret = 0;
    for(int i = 0; i < ny; ++i)
        if(~linker[i]) //有匹配
            ret += g[linker[i]][i]; //加上权值
    return ret;
}

void solve()
{
    cout << KM() << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    while(cin >> n)
    {
        read();
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/HNUCSEE_LJK/article/details/100097943