HDU 2255 奔小康赚大钱(KM算法模板题)

135 篇文章 0 订阅
109 篇文章 0 订阅

题目地址
题意:中文。
思路:因为每个村民都有自己心仪的房子,然后村委会要赚最多的钱,所以这就是一个带权二分图的最佳匹配,解决这一问题的算法就是KM算法,这题就是直接套模板就好了。
推荐:一篇KM算法我觉得讲的蛮好的博客

#include <iostream>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <iomanip>
#define N 10100
#define M 500010
#define LL __int64
#define inf 0x3f3f3f3f
#define lson l,mid,ans<<1
#define rson mid+1,r,ans<<1|1
#define getMid (l+r)>>1
#define movel ans<<1
#define mover ans<<1|1
using namespace std;
const LL mod = 1e9 + 7;
int mapp[N][N];//表明好感度
struct Kuhn_Munkras {
    int nx, ny;
    int linker[N];//每个y所对应的x
    int lx[N];//x的期望值
    int ly[N];//y的期望值
    int slack[N];//每个y能被x匹配的最小还需的期望值
    bool visx[N], visy[N];//匹配过的标记
    void init(int n, int m) {
        this->nx = n;
        this->ny = m;
        memset(linker, -1, sizeof(linker));//每个y最初都没有匹配
        memset(ly, 0, sizeof(ly));//最初每个y的期望值都是0
    }
    bool dfs(int x) {
        visx[x] = true;
        for (int y = 1; y <= ny; y++) {
            if (!visy[y]) {
                int tmp = lx[x] + ly[y] - mapp[x][y];
                if (tmp == 0) {//如果两边的期望值相加等于好感度则符合要求
                    visy[y] = true;
                    if (linker[y] == -1 || dfs(linker[y])) {//没有被匹配过的y,或者能换匹配的y
                        linker[y] = x;
                        return true;
                    }
                }
                else if (slack[y]>tmp) {
                    slack[y] = tmp; //可以理解为该y要得到x的匹配,还需多少期望值,取最小值
                }
            }
        }
        return false;
    }
    int solve(int n, int m) {
        init(n, m);
        for (int i = 1; i <= nx; i++) {
            lx[i] = -inf;
            for (int j = 1; j <= ny; j++) {
                if (mapp[i][j]>lx[i]) {
                    lx[i] = mapp[i][j];//取最大的好感度为x的期望值
                }
            }
        }
        for (int x = 1; x <= nx; x++) {//尝试为每一个x解决匹配问题
            memset(slack, inf, sizeof(slack));
            while (true) {//找不到就降低期望值
                memset(visx, false, sizeof(visx));
                memset(visy, false, sizeof(visy));
                if (dfs(x)) {//找到就离开
                    break;
                }
                int d = inf;
                for (int i = 1; i <= ny; i++) {
                    if (!visy[i] && d>slack[i]) {//找到最小的差值
                        d = slack[i];
                    }
                }
                for (int i = 1; i <= nx; i++) {//对所有访问过的x减小期望值
                    if (visx[i]) {
                        lx[i] -= d;
                    }
                }
                for (int i = 1; i <= ny; i++) {//对所有访问过的y增加期望值
                    if (visy[i]) {
                        ly[i] += d;
                    }
                    else {
                        slack[i] -= d;
                    }
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= ny; i++) {
            if (linker[i] != -1) {//匹配成功的求和
                res += mapp[linker[i]][i];
            }
        }
        return res;
    }
}KM;
int main() {
    int n;
    cin.sync_with_stdio(false);
    while (cin >> n) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                cin >> mapp[i][j];
            }
        }
        cout << KM.solve(n, n) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值