P3980 [NOI2008]志愿者招募 (费用流)

题意:最多1000天 每天需要至少ai个工人施工 有10000种工人可以雇佣

   每种工人可以工作si到ti天 雇佣一个的花费是ci 问怎样安排使得施工花费最少

思考:最直白的建模方式 就是每种工人可以和他能工作的天 连边

   但是这样就引出了一个一对多的问题 一种工人对他所连的所有天 贡献是一样的

   也就是说他流向和他连的天的 流量应该都是一样的 但是网络流显然是做不到这一点

  

   于是我们重新思考 发现每种工人其实就是一种区间覆盖 那么我们考虑差分的思想

   把每相邻两天连起来 表示这种工人在第i天工作了后 跑到第i+1天去工作了

   因为每种工人最多工作到ti天 所以我们要考虑某种方式在ti+1天把si流进来的流量放出去 他不能对ti+1天有贡献

   然后就不会了....

题解:把每一天当作点 今天向明天连一条 容量INF-ai 花费0的边

   对于每种工人 从si天向ti+1天 连 容量为INF 花费为ci的边

   s连1 容量INF花费0  n+1连t 容量INF 花费0

   跑一遍最大流 因为一定有完成施工的方案 所以能满流 得到的最小花费就是答案

   为什么每两天之间的容量是INF-ai 表示今天需要至少ai的流量从带权边补足到INF

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

int n, m, cnt, mincost, s, t;
struct node {
    int to, nex, val, cost;
}E[30005];
int head[1005];
int cur[1005];
int a[1005];

void addedge(int x, int y, int va, int cos) {
    E[++cnt].to = y; E[cnt].nex = head[x]; head[x] = cnt; E[cnt].val = va; E[cnt].cost = cos;
    E[++cnt].to = x; E[cnt].nex = head[y]; head[y] = cnt; E[cnt].val = 0; E[cnt].cost = -cos;
}

int dis[1005], inque[1005], vis[1015];
bool spfa() {
    for(int i = 1; i <= t; i++) dis[i] = INF, inque[i] = 0, cur[i] = head[i];
    queue<int> que; que.push(s);
    dis[s] = 0; inque[s] = 1;

    while(!que.empty()) {
        int u = que.front(); que.pop();
        inque[u] = 0;

        for(int i = head[u]; i; i = E[i].nex) {
            int v = E[i].to;
            if(E[i].val && dis[v] > dis[u] + E[i].cost) {
                dis[v] = dis[u] + E[i].cost;
                if(!inque[v]) {
                    inque[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    return dis[t] != INF;
}


int dfs(int x, int flow) {
    if(x == t) {
        vis[t] = 1;
        return flow;
    }

    vis[x] = 1;
    int used = 0, rflow = 0;
    for(int i = cur[x]; i; i = E[i].nex) {
        cur[x] = i;
        int v = E[i].to;
        if(E[i].val && dis[v] == dis[x] + E[i].cost && (!vis[v] || v == t)) {
            if(rflow = dfs(v, min(E[i].val, flow - used))) {
                used += rflow;
                E[i].val -= rflow;
                E[i ^ 1].val += rflow;
                mincost += rflow * E[i].cost;
                if(used == flow) break;
            }
        }
    }
    return used;
}

void dinic() {
    mincost = 0;
    while(spfa()) {
        vis[t] = 1;
        while(vis[t]) {
            memset(vis, 0, sizeof(int) * (t + 1));
            dfs(s, INF);
        }
    }
}

int main() {
    cnt = 1;
    scanf("%d%d", &n, &m);
    s = n + 2; t = s + 1;
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= m; i++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        addedge(a, b + 1, INF, c);
    }
    for(int i = 1; i <= n; i++) addedge(i, i + 1, INF - a[i], 0);
    addedge(s, 1, INF, 0); addedge(n + 1, t, INF, 0);
    dinic();
    printf("%d\n", mincost);
    return 0;
}
View Code

 

posted @ 2019-10-15 18:07  lwqq3  阅读(124)  评论(0编辑  收藏  举报