BZOJ 3550 ONTAK2010 Vacation 线性规划转费用流

9 篇文章 0 订阅
1 篇文章 0 订阅

题目大意

给出一个长度为 3×N  的序列,规定 N  个数字中不能选择超过k 个,问最多能取出的数的权值和是多少。

思路

非常神的建图,本来想朴素费用流不过去学zkw费用流,结果朴素费用流跑的飞起。
利用一些辅助变量我们可以列出一些式子:
k  N 个数种最多能够取出的数的数量, a[i]  表示第 i  天选不选,y 数组一定是自然数。
 N i=1 a[i]+y[1]=k 
 N+1 i=2 a[i]+y[2]=k 
 N+2 i=3 a[i]+y[3]=k 
 
 3N i=2N+1 a[i]+y[2N+1]=k 
保持第一个式子和最后一个式子不变,剩下的式子上下作差,得到如下的式子:
 N i=1 a[i]+y[1]=k 
y[1]+a[1]=y[2]+a[N+1] 
y[2]+a[2]=y[3]+a[N+2] 
 
y[N+1]+a[N+1]=y[N+2]+a[2N+1] 
 
y[2N]+a[2N]=y[2N+1]+a[3N] 
 3N i=2N+1 a[i]+y[2N+1]=k 
现在看就比较明显了,把第一个式子和最后一个式子分别看成是源点和汇点,其他的式子和看成是点,变量有相关连的就连边,注意每个变量的原始意义。
每个式子拆成两半,等号表示流量守恒,左边表示流入,右边表示流出,费油就是读入的权值。之后就是最大费用最大流了。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXP 10000
#define MAXE 1000000
#define S 1
#define SS (MAXP - 2)
#define T (cnt * 2 + 2)
#define TT (MAXP - 3)
#define INF 0x3f3f3f3f
using namespace std;

int cnt, k;
int src[MAXP];

struct MinCostMaxFlow{
    int head[MAXP], total;
    int _next[MAXE], aim[MAXE], flow[MAXE], cost[MAXE];

    int f[MAXP], from[MAXP], p[MAXP];
    bool v[MAXP]; 

    MinCostMaxFlow():total(1) {}
    void Add(int x, int y, int f, int c) {
        _next[++total] = head[x];
        aim[total] = y;
        flow[total] = f;
        cost[total] = c;
        head[x] = total;
    }
    void Insert(int x, int y, int f, int c) {
        Add(x, y, f, -c);
        Add(y, x, 0, c);
    }
    bool SPFA() {
        static queue<int> q;
        while(!q.empty())   q.pop();
        memset(f, 0x3f, sizeof(f));
        memset(v, false, sizeof(v));
        f[SS] = 0;
        q.push(SS);
        while(!q.empty()) {
            int x = q.front(); q.pop();
            v[x] = false;
            for(int i = head[x]; i; i = _next[i])
                if(flow[i] && f[aim[i]] > f[x] + cost[i]) {
                    f[aim[i]] = f[x] + cost[i];
                    if(!v[aim[i]])
                        v[aim[i]] = true, q.push(aim[i]);
                    from[aim[i]] = x;
                    p[aim[i]] = i;
                }
        }
        return f[TT] != INF;
    }
    int EdmondsKarp() {
        int re = 0;
        while(SPFA()) {
            int max_flow = INF;
            for(int i = TT; i != SS; i = from[i])
                max_flow = min(max_flow, flow[p[i]]);
            for(int i = TT; i != SS; i = from[i]) {
                flow[p[i]] -= max_flow;
                flow[p[i]^1] += max_flow;
            }
            re += max_flow * f[TT];
        }
        return re;
    }
}solver;


int main()
{
    cin >> cnt >> k;
    for(int i = 1; i <= cnt * 3; ++i)
        scanf("%d", &src[i]);
    solver.Insert(SS, S, k, 0);
    solver.Insert(T, TT, k, 0);
    for(int i = 2; i <= cnt + 1; ++i)
        solver.Insert(S, i, 1, src[i - 1]);
    for(int i = cnt + 2; i <= cnt * 2 + 1; ++i)
        solver.Insert(i - cnt, i, 1, src[i - 1]);
    for(int i = cnt + 2; i <= cnt * 2 + 1; ++i)
        solver.Insert(i, (cnt << 1) + 2, 1, src[i - 1 + cnt]);
    for(int i = 2; i <= cnt * 2 + 2; ++i)
        solver.Insert(i - 1, i, k, 0);
    cout << -solver.EdmondsKarp() << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值