bzoj1061 建图 + 最小费用流

Posted hugh-locke

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1061 建图 + 最小费用流相关的知识,希望对你有一定的参考价值。

https://www.lydsy.com/JudgeOnline/problem.php?id=106152

对于一个点对上多个点,不太容易建图的时候,考虑逆向思考

技术分享图片
申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要
Ai 个人。 布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用
是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这
并不是他的特长!于是布布找到了你,希望你帮他设计一种最优的招募方案。
Input
  第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。 接下来的一行中包含N 个非负
整数,表示每天至少需要的志愿者人数。 接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了
方便起见,我们可以认为每类志愿者的数量都是无限多的。
Output
  仅包含一个整数,表示你所设计的最优方案的总费用。

Sample Input
3 3
2 3 4
1 2 2
2 3 5
3 3 2
Sample Output
14
Hint
1 ≤ N ≤ 10001 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1
题意

虽然我读完题知道这是一个费用流,但是始终想不到应该怎么建图,在我觉得这题不可能建图的时候看了一下题解。

才知道世界之大无奇不有。

原本正常的建图是以源点连到左边,志愿者连到他对应的天数,但是首先边会很多,并且费用也很难表示,建图的过程显得困难重重。

但是如果换一种方法。

我们将这个点需要多少个志愿者变成这个点还差多少个志愿者到达标准,标准我们设为一个大常数U。看起来好像没有区别,但是事实上我们从源点出U,经过每一天相当于经过了U - a[i]的限制,流量会减少,我们需要用志愿者来使她满流出去,所以对于一个L到R的志愿者,我们建立一条L 到 R + 1,费用为W的边即可,相当于这个志愿者填上了L 到 R的空缺。

真是神奇啊

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
inline int read(){int now=0;register char c=getchar();for(;!isdigit(c);c=getchar());
for(;isdigit(c);now=now*10+c-0,c=getchar());return now;}
#define For(i, x, y) for(int i=x;i<=y;i++)  
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Sca2(x,y) scanf("%d%d",&x,&y)
#define Sca3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d
", x)
#define Prl(x) printf("%lld
",x);  
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 1e5 + 10;
const int maxm = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const int U = 1e5 + 10;
const int mod = 1e9 + 7; 
int N,M,K;
struct MCMF{
    struct Edge{
        int from,to,cap,flow,nxt;
        LL cost;
        Edge() {}
        Edge(int x,int y,int z,int u,LL v,int n){
            from = x; to = y; cap = z; flow = u; cost = v; nxt = n;
        }
    }edge[maxm];
    int E,n,head[maxn];
    int inq[maxn],d[maxn],p[maxn],a[maxn];
    inline void init(int _n){
        n = _n;E = 0;
        memset(head,-1,sizeof(head));
    }
    inline void addEdge(int f,int t,int c,LL w){
        edge[E] = Edge(f,t,c,0,w,head[f]);
        head[f] = E++;
        edge[E] = Edge(t,f,0,0,-w,head[t]);
        head[t] = E++;
    }
    bool spfa(int s,int t,int &flow,LL &cost){
        for(int i = 0 ; i <= n ; i ++) d[i] = INF;
        memset(inq,0,sizeof(inq));
        d[s] = 0;inq[s] = 1;p[s] = 0;a[s] = INF;
        queue<int>Q; Q.push(s);
        while(!Q.empty()){
            int u = Q.front(); Q.pop(); inq[u] = 0;
            for(int i = head[u]; ~i; i = edge[i].nxt){
                Edge &e = edge[i];
                if(e.cap <= e.flow || d[e.to] <= d[u] + e.cost) continue;
                d[e.to] = d[u] + e.cost;
                p[e.to] = i;
                a[e.to] = min(a[u],e.cap - e.flow);
                if(!inq[e.to]){
                    Q.push(e.to);
                    inq[e.to] = 1;
                }
            }
        }
        if(d[t] == INF) return false;
        flow += a[t];
        cost += (LL)d[t] * (LL)a[t];
        for(int u = t; u != s; u = edge[p[u]].from){
            edge[p[u]].flow += a[t];
            edge[p[u] ^ 1].flow -= a[t];
        }
        return true;
    }

    int mcmf(int s,int t,LL &cost){
        int flow = 0;
        cost = 0;
        while(spfa(s,t,flow,cost));
        return flow;
    }
}g;


int main()
{
    Sca2(N,M);
    int s = 0,t = N + 1;
    g.init(t + 1); g.addEdge(s,1,U,0);
    For(i,1,N){
        LL x; Scl(x);
        g.addEdge(i,i + 1,U - x,0);
    }
    while(M--){
        int l,r; LL c;
        Sca2(l,r); Scl(c);
        g.addEdge(l,r + 1,INF,c);
    }
    LL cost = 0; g.mcmf(s,t,cost);
    Prl(cost);
    #ifdef VSCode
    system("pause");
    #endif
    return 0;
}

 

以上是关于bzoj1061 建图 + 最小费用流的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1061: [Noi2008]志愿者招募最小费用最大流

BZOJ-2879美食节 最小费用最大流 + 动态建图

bzoj 1061 志愿者招募 有上下界费用流做法

[bzoj1061]志愿者招募

志愿者招募 HYSBZ - 1061(公式建图费用流)

cf1061E Politics (费用流)