HDU 6346 整数规划(KM)

题目链接
题意

给定 n × n n×n n×n 个整数 a i j ( 1 ≤ i , j ≤ n ) a_{ij}(1≤i,j≤n) aij(1i,jn),要找出 2 n 2n 2n 个整数 x 1 , x 2 , … , x n , y 1 , y 2 , … , y n x1,x2,…,xn,y1,y2,…,yn x1,x2,,xn,y1,y2,,yn 在满足 x i + y j ≤ a i , j ( 1 ≤ i , j ≤ n ) xi+yj≤a_{i,j}(1≤i,j≤n) xi+yjai,j(1i,jn) 的约束下,最大化目标函数 ∑ i = 1 n ( x i + y i ) \sum_{i=1}^n(xi+yi) i=1n(xi+yi)
求目标函数的最大值。

思路

找这题主要测试KM复杂度,dfs版找slack是伪 N 3 N^3 N3,这里记个bfs板子,KM学习点此处
KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的,顶标满足 x i + y j > = w [ i ] [ j ] x_i+y_j>=w[i][j] xi+yj>=w[i][j],求出其中最大的顶标和(即最大权匹配)

这题所约束的条件与KM中顶标性质相同,不过符号反了,那么交换等式左右两边即可。即反转w,反转顶标和。

代码
// 注意:如果只是想求最大权值匹配而不要求是完全匹配的话,请把各个不相连的边的权值设置为0。
#include<bits/stdc++.h>
using namespace std;

#define ll long long

const ll N = 205;
const ll INF = 0x3f3f3f3f3f3f3f3f;

struct KM {
    ll n;
    ll e[N][N];
    ll wx[N], wy[N], slack[N];
    ll nxtx[N], nxty[N], visx[N], visy[N], pre[N], q[N], ql, qr;
    ll check(ll now) {
        visy[now] = 1;
        if(nxty[now] != -1) {
            if(!visx[nxty[now]]) q[++qr] = nxty[now], visx[nxty[now]] = 1;
            return 0;
        }
        while(now != -1) swap(now, nxtx[nxty[now] = pre[now]]);
        return 1; // 找到增广路
    }
    void bfs(ll s) {
        memset(slack,0x3f,sizeof(slack));
        memset(visx,0,sizeof(visx));
        memset(visy,0,sizeof(visy));
        q[ql = qr = 1] = s;
        visx[s] = qr = 1;
        ll d;
        while(1) {
            while(ql <= qr) {
                ll u = q[ql++];
                for(ll i = 1; i <= n; ++i) {
                    if(visy[i]) continue;
                    ll value = wx[u]+wy[i]-e[u][i];
                    if(slack[i] >= value) {
                        slack[i] = value;
                        pre[i] = u;
                        if(slack[i] == 0 && check(i)) return;
                    }
                }
            }
            ll d = INF;
            for(ll i = 1; i <= n; ++i) if(!visy[i] && slack[i]) d = min(d,slack[i]);
            for(ll i = 1; i <= n; ++i) {
                if(visx[i]) wx[i] -= d;
                if(visy[i]) wy[i] += d;
                else slack[i] -= d;
            }
            ql = 1, qr = 0;
            for(ll i = 1; i <= n; ++i) if(!visy[i] && !slack[i] && check(i)) return;
        }
    }
    ll getans() {
        memset(nxtx,-1,sizeof(nxtx));
        memset(nxty,-1,sizeof(nxty));
        memset(wy,0,sizeof(wy));
        for(ll i = 1; i <= n; ++i) wx[i] = *max_element(e[i]+1,e[i]+1+n);
        for(ll i = 1; i <= n; ++i) bfs(i);
        ll ans = 0;
        for(ll i = 1; i <= n; ++i) if(nxtx[i] != -1) ans += e[i][nxtx[i]];
// 本题所求是顶标和,顶标和等于最大匹配
//        for(ll i = 1; i <= n; ++i) ans += wx[i]+wy[i];
        return ans;
    }
}ac;

int main() {
    ll t, ca = 1;
    for(scanf("%lld",&t); ca <= t; ++ca) {
        scanf("%lld",&ac.n);
        for(ll i = 1; i <= ac.n; ++i) {
            for(ll j = 1; j <= ac.n; ++j) {
                scanf("%lld",&ac.e[i][j]);
                ac.e[i][j] = -ac.e[i][j];
            }
        }
        printf("Case #%lld: %lld\n",ca,-ac.getans());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值