zoukankan      html  css  js  c++  java
  • 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
    ", mincost);
        return 0;
    }
    View Code
  • 相关阅读:
    POJ 1840 Eqs 二分+map/hash
    【vijos】P1514天才的记忆
    函数介绍
    函数参数和函数返回值
    函数参数和函数返回值
    前端项目里常见的十种报错及其解决办法
    前端项目里常见的十种报错及其解决办法
    BootstrapTable的使用教程
    BootstrapTable的使用教程
    前端js实现打印(导出)excel表格
  • 原文地址:https://www.cnblogs.com/lwqq3/p/11679373.html
Copyright © 2011-2022 走看看