BZOJ 1061: [Noi2008]志愿者招募

1061: [Noi2008]志愿者招募

Time Limit: 20 Sec  Memory Limit: 162 MB
Submit: 4064  Solved: 2476
[Submit][Status][Discuss]

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。

Source

分析:

%LYD...

这道题普遍做法貌似都是线性规划单纯形的做法,LYD告诉我们可以跑上下界最小费用可行流...

我们把每一天看成一个点,然后从i向i+1连边,上界为inf,下界为ai,费用为0...

然后对于每一类志愿者,从ti+1到si连边,上界为inf,下界为0,费用为ci...这样每花费ci的代价,就从si到ti增加一个流...

然后就转化成了无源汇上下界最小费用可行流,其实就是把无源汇上下界可行流的最大流转化成最小费用最大流...

怎么求无源汇上下界可行流?

如果把C-B作为容量上界,0作为容量下界,就是一般的网络流模型。

然而求出的实际流量为f(u,v)+B(u,v),不一定满足流量守恒,需要调整。

设inB[u]=∑B(i,u),outB[u]=∑B(u,i),d[u]=inB[u]-outB[u]。

新建源汇,S向d>0的点连边,d<0的点向汇点连边,容量为相应的d。 在该网络上求最大流,则每条边的流量+下界就是原网络的一个可行流。

代码:

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<queue>
 6 //by NeighThorn
 7 #define inf 0x3f3f3f3f
 8 #define INF 0x3f3f3f3f3f3f
 9 using namespace std;
10 
11 const int maxn=1000+5,maxm=100000+5;
12 
13 int n,m,S,T,cnt,w[maxm],hd[maxn],fl[maxm],to[maxm],nxt[maxm],Min[maxn],vis[maxn],from[maxn];
14 
15 long long dis[maxn],dif[maxn];
16 
17 inline bool spfa(void){
18     for(int i=S;i<=T;i++)
19         dis[i]=INF,Min[i]=inf;
20     queue<int> q;q.push(S),vis[S]=1,dis[S]=0;
21     while(!q.empty()){
22         int top=q.front();q.pop();vis[top]=0;
23         for(int i=hd[top];i!=-1;i=nxt[i])
24             if(dis[to[i]]>dis[top]+w[i]&&fl[i]){
25                 from[to[i]]=i;
26                 dis[to[i]]=dis[top]+w[i];
27                 Min[to[i]]=min(Min[top],fl[i]);
28                 if(!vis[to[i]])
29                     vis[to[i]]=1,q.push(to[i]);
30             }
31     }
32     return dis[T]!=INF;
33 }
34 
35 inline long long find(void){
36     for(int i=T;i!=S;i=to[from[i]^1])
37         fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T];
38     return dis[T]*Min[T];
39 }
40 
41 inline int dinic(void){
42     int res=0;
43     while(spfa())
44         res+=find();
45     return res;
46 }
47 
48 inline void add(int l,int s,int x,int y){
49     w[cnt]=l;fl[cnt]=s;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
50     w[cnt]=-l;fl[cnt]=0;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++;
51 }
52 
53 signed main(void){
54     // freopen("in.txt","r",stdin);
55     memset(hd,-1,sizeof(hd));
56     scanf("%d%d",&n,&m);S=0,T=n+2;
57     for(int i=1,y;i<=n;i++)
58         scanf("%d",&y),add(0,inf,i,i+1),dif[i]-=y,dif[i+1]+=y;
59     for(int i=1,s,x,y;i<=m;i++)
60         scanf("%d%d%d",&x,&y,&s),add(s,inf,y+1,x);
61     for(int i=1;i<=n+1;i++){
62         if(dif[i]>0)
63             add(0,dif[i],S,i);
64         else if(dif[i]<0)
65             add(0,-dif[i],i,T);
66     }
67     printf("%d\n",dinic());
68     return 0;   
69 }
View Code

By NeighThorn

posted @ 2017-01-06 09:47  NeighThorn  阅读(511)  评论(0编辑  收藏  举报