bzoj1061 [Noi2008]志愿者招募

Posted wfj_2048

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1061 [Noi2008]志愿者招募相关的知识,希望对你有一定的参考价值。

Description

  申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难
题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要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 ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均 不超过2^31-1。

正解:不知道。。据说原本的正解线性规划被hack掉了。。

然而我连线性规划都不会,所以写了一个naive的费用流。。学了以后再来填坑吧。。

考虑从原点向第0天连容量为inf,费用为0的边,第i天向第i+1天连容量为inf-a[i],费用为0的边,我们把这条线称之为主线。第n天向汇点连容量为inf,费用为0的边。对于每一段区间,从第s-1天到第t天连容量为inf,费用为c的边,我们把这条线称为副线。那么跑费用流时,主线上的堵塞流就自动地转移到副线上,同时,副线又因为有费用,那么我们就可以算出最小的费用了。

 

//It is made by wfj_2048~
#include <algorithm>
#include <iostream>
#include <complex>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#define inf (1<<29)
#define il inline
#define RG register
#define ll long long
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)

using namespace std;

struct edge{ int nt,to,flow,cap,cost; }g[30010];


queue <int> Q;
int head[1010],dis[1010],flow[1010],fa[1010],p[1010],a[1010],n,m,S,T,num=1,cost;

il int gi(){
    RG int x=0,q=1; RG char ch=getchar(); while ((ch<0 || ch>9) && ch!=-) ch=getchar();
    if (ch==-) q=-1,ch=getchar(); while (ch>=0 && ch<=9) x=x*10+ch-48,ch=getchar(); return q*x;
}

il void insert(RG int from,RG int to,RG int cap,RG int cost){ g[++num]=(edge){head[from],to,0,cap,cost},head[from]=num; }

il int bfs(RG int S,RG int T){
    for (RG int i=1;i<=n+3;++i) dis[i]=inf;
    Q.push(S),flow[S]=inf,dis[S]=0;
    while (!Q.empty()){
    RG int x=Q.front(),v; Q.pop();
    for (RG int i=head[x];i;i=g[i].nt){
        v=g[i].to;
        if (dis[v]>dis[x]+g[i].cost && g[i].cap>g[i].flow){
        flow[v]=min(flow[x],g[i].cap-g[i].flow);
        dis[v]=dis[x]+g[i].cost; fa[v]=x,p[v]=i,Q.push(v);
        }
    }
    }
    if (dis[T]==inf) return 0; cost+=flow[T]*dis[T];
    for (RG int x=T;x!=S;x=fa[x]) g[p[x]].flow+=flow[T],g[p[x]^1].flow-=flow[T];
    return 1;
}

il int mcmf(RG int S,RG int T){ cost=0; while (bfs(S,T)); return cost; }

il void work(){
    n=gi(),m=gi(); S=n+2,T=n+3; insert(S,1,inf,0),insert(1,S,0,0);
    for (RG int i=1;i<=n;++i) a[i]=gi(),insert(i,i+1,inf-a[i],0),insert(i+1,i,0,0);
    for (RG int i=1;i<=m;++i){
    RG int s=gi(),t=gi(),c=gi();
    insert(s,t+1,inf,c),insert(t+1,s,0,-c);
    }
    insert(n+1,T,inf,0),insert(T,n+1,0,0);
    printf("%d\n",mcmf(S,T)); return;
}

int main(){
    File("volunteer");
    work();
    return 0;
}

 

以上是关于bzoj1061 [Noi2008]志愿者招募的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1061: [Noi2008]志愿者招募 [单纯形法]

bzoj1061: [Noi2008]志愿者招募

bzoj 1061: [Noi2008]志愿者招募

BZOJ1061:[NOI2008]志愿者招募——题解

bzoj 1061 [Noi2008]志愿者招募(数学模型,MCMF)

bzoj1061 [Noi2008]志愿者招募