[BZOJ1061][NOI2008]志愿者招募 费用流

列出几个线性规划的式子
添加变量把不等式变成流量恒等式
按照式子建图,跑最小费用最大流
答案即为费用
https://www.byvoid.com/zhs/blog/noi-2008-employee

/**************************************************************
    Problem: 1061
    User: di4CoveRy
    Language: C++
    Result: Accepted
    Time:2152 ms
    Memory:5256 kb
****************************************************************/

#include <bits/stdc++.h>
#define INF (1<<29)
#define N 2050 
#define M 200050
using namespace std;
typedef long long LL;

int head[N],cnt=1,tot,S,T;
int p[N],rp[N],L[N],a,b,c[N],d;
LL dis[N];
int n,m,k;
LL ans;

inline int rd() {
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct Edge{ int a,b,v,cost,next; }e[M],id;
inline void add(int a,int b,int v,int cost) {
    if (!a || !b) return ; 
    e[++cnt] = (Edge){ a,b,v,cost,head[a] }, head[a] = cnt;
    e[++cnt] = (Edge){ b,a,0,-cost,head[b] },head[b] = cnt;
}

#define cp e[i].v  
#define B e[i].b  

bool SPFA() {  
    bool flag = false;
    for (int i=1;i<=tot;i++) p[i] = 0, dis[i] = (1LL<<62);
    dis[S] = 0;
    queue<int> q; q.push(S);  
    while (!q.empty()) {  
        int u = q.front(); q.pop();  
        if (u == T) flag = true;
        for (int i=head[u];i;i=e[i].next)  
            if (cp > 0 && dis[u] + e[i].cost < dis[B]) {  
                dis[B] = dis[u] + e[i].cost;  
                p[B] = i;  
                q.push(B);  
            }   
    }  
    return flag;  
}  

void mcf() {  
    int g = p[T] , flow = INF;  
    while (g) {
        flow = min(flow , e[g].v);  
        g = p[ e[g].a ];  
    }
    g = p[T];  
    while (g) {  
        e[g  ].v -= flow;  
        e[g^1].v += flow;  
        ans += 1LL * e[g].cost * flow;  
        g = p[ e[g].a ];  
    }
}

int main() {
    n = rd(), m = rd();
    for (int _=1;_<=n;_++) p[_] = rd();
    for (int _=1;_<=n;_++) rp[_] = p[_]-p[_-1];

    S = ++tot, T = ++tot;
    for (int _=0;_<=n+1;_++) L[_] = ++tot;
    for (int _=1;_<=n;_++)
        if (rp[_] > 0) add(S, L[_], rp[_], 0); else add(L[_], T, -rp[_], 0);

    for (int _=1;_<=n;_++) add(L[_+1], L[_], INF, 0);
    add(L[n+1], T, p[n], 0);

    for (int _=1;_<=m;_++) {
        int a = rd(), b = rd(), v = rd();
        add(L[a], L[b+1], INF, v);
    }

    while (SPFA()) mcf();
    cout << ans << endl;
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值