[NOI2008]志愿者招募

题面

link

分析

看到要求每天最少a[i]人 那么就反过来
使流量最大为inf - a[i]
因为是以天来限制人数 所以每天作为一条边
S --inf / 0 --> 第一天的起点 node[1]
最后一天终点 node[n + 1] -- inf / 0 --> T
第i天: node[i] -- inf / a[i] --> node[i + 1]

志愿者是补流 为了使最大流为inf
那么使node[s[i]] -- inf / c[i] --> node[t[i] + 1]
跑最小费用最大流即可

// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std;
/*
建图:S --inf / 0 --> 第一天的起点 node[1]
最后一天终点  node[n + 1] -- inf / 0 --> T
第i天:  node[i] -- inf / a[i] --> node[i + 1]
志愿者是补流 为了使最大流为inf 
那么使node[s[i]] -- inf / c[i] --> node[t[i] + 1]
*/
#define node(x, y) n + (x - 1) * ptot + (y)
typedef long long ll;
const ll inf = 1e11;
const int N = 1e3 + 5;
const int E = 1e6; 
struct Edge{
    int v, next; ll f, w;
}edge[E];
int esize = 1, head[N], S, T, n, m;
ll mxf, mc;
int fro, pre[N]; 
ll dis[N], fl[N], p[45];
queue<int> que;

inline void addedge(int x, int y, ll f, ll w){
    edge[++esize] = (Edge){y, head[x], f, w}, head[x] = esize;
    edge[++esize] = (Edge){x, head[y], 0, -w}, head[y] = esize;
    //printf("%d %d %lld %lld\n", x, y, f, w);
}

inline bool spfa(){
    for(int i = 1; i <= T; ++i) dis[i] = inf;
    que.push(S), dis[S] = 0, fl[S] = inf;
    while(!que.empty()){
        fro = que.front(), que.pop();
        for(int i = head[fro], vv; ~i; i = edge[i].next){
            vv = edge[i].v;
            if(edge[i].f > 0 && dis[vv] > dis[fro] + edge[i].w)
                dis[vv] = dis[fro] + edge[i].w, pre[vv] = i, fl[vv] = min(edge[i].f, fl[fro]), que.push(vv);
        }
    }
    return dis[T] != inf;
}

inline void update(){
    int i = T, j;
    while(i != S){
        j = pre[i], edge[j].f -= fl[T], 
        edge[j ^ 1].f += fl[T], i = edge[j ^ 1].v;
    } 
    mxf += fl[T], mc += fl[T] * dis[T];
} 

void EK(){
    while(spfa()) update();
}

int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    S = n + 2, T = n + 3;//! 
    for(int i = 1, x; i <= n; ++i)
        scanf("%d", &x), addedge(i, i + 1, inf - x, 0); 
    addedge(S, 1, inf, 0), addedge(n + 1, T, inf, 0);
    for(int i = 1, x, y, z; i <= m; ++i)
        scanf("%d%d%d", &x, &y, &z), addedge(x, y + 1, inf, z);
    EK();
    printf("%lld", mc);
    return 0; 
}

转载于:https://www.cnblogs.com/hjmmm/p/11281325.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值