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的边,我们把这条线称为副线。那么跑费用流时,主线上的堵塞流就自动地转移到副线上,同时,副线又因为有费用,那么我们就可以算出最小的费用了。

 

 1 //It is made by wfj_2048~
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <complex>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <cstdio>
 8 #include <vector>
 9 #include <cmath>
10 #include <queue>
11 #include <stack>
12 #include <map>
13 #include <set>
14 #define inf (1<<29)
15 #define il inline
16 #define RG register
17 #define ll long long
18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
19 
20 using namespace std;
21 
22 struct edge{ int nt,to,flow,cap,cost; }g[30010];
23 
24 
25 queue <int> Q;
26 int head[1010],dis[1010],flow[1010],fa[1010],p[1010],a[1010],n,m,S,T,num=1,cost;
27 
28 il int gi(){
29     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
30     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
31 }
32 
33 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; }
34 
35 il int bfs(RG int S,RG int T){
36     for (RG int i=1;i<=n+3;++i) dis[i]=inf;
37     Q.push(S),flow[S]=inf,dis[S]=0;
38     while (!Q.empty()){
39     RG int x=Q.front(),v; Q.pop();
40     for (RG int i=head[x];i;i=g[i].nt){
41         v=g[i].to;
42         if (dis[v]>dis[x]+g[i].cost && g[i].cap>g[i].flow){
43         flow[v]=min(flow[x],g[i].cap-g[i].flow);
44         dis[v]=dis[x]+g[i].cost; fa[v]=x,p[v]=i,Q.push(v);
45         }
46     }
47     }
48     if (dis[T]==inf) return 0; cost+=flow[T]*dis[T];
49     for (RG int x=T;x!=S;x=fa[x]) g[p[x]].flow+=flow[T],g[p[x]^1].flow-=flow[T];
50     return 1;
51 }
52 
53 il int mcmf(RG int S,RG int T){ cost=0; while (bfs(S,T)); return cost; }
54 
55 il void work(){
56     n=gi(),m=gi(); S=n+2,T=n+3; insert(S,1,inf,0),insert(1,S,0,0);
57     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);
58     for (RG int i=1;i<=m;++i){
59     RG int s=gi(),t=gi(),c=gi();
60     insert(s,t+1,inf,c),insert(t+1,s,0,-c);
61     }
62     insert(n+1,T,inf,0),insert(T,n+1,0,0);
63     printf("%d\n",mcmf(S,T)); return;
64 }
65 
66 int main(){
67     File("volunteer");
68     work();
69     return 0;
70 }

 

这题用线性规划,设第i种志愿者为xi,则根据样例,必须满足:

min{2x1+5x2+2x3},x1>=2,x1+x2>=3,x2+x3>=4,x1,x2,x3>=0。

但是这不是线性规划的标准型,没有基本可行解,所以我们可以考虑对偶变换。

给定一个最大化目标的线性规划,我们应该描述如何形式化一个对偶线性规划,其中目标是最小化,而且最优值与初始线性规划的最优值相同。当表示对偶性规划时,我们称初始的线性规划为原始线性规划。为了构造对偶问题,我们将最大化改为最小化,交换右边系数与目标系数,并且将小于等于改为大于等于。原始问题的m个越是,每一个在对偶问题中都有一个对应的变量yi,对偶问题的n个约束,每一个在原始问题中都有一个对应的变量xj。

——算法导论

然后我们就可以把上面的线性规划变成:

max{2x1+3x2+4x3},x1+x2<=2,x2+x3<=5,x3<=2,x1,x2,x3>=0。

这样,我们就可以直接套用模板了。

 

 1 //It is made by wfj_2048~
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <complex>
 5 #include <cstring>
 6 #include <cstdlib>
 7 #include <cstdio>
 8 #include <vector>
 9 #include <cmath>
10 #include <queue>
11 #include <stack>
12 #include <map>
13 #include <set>
14 #define eps (1e-12)
15 #define inf (1e15)
16 #define il inline
17 #define RG register
18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
19 
20 using namespace std;
21 
22 double a[10010][1010],ans;
23 int b[1010],n,m;
24 
25 il int gi(){
26     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
27     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
28 }
29 
30 il void pivot(RG int l,RG int e){
31     RG double k=a[l][e]; a[l][e]=1;
32     for (RG int i=0;i<=n;++i) a[l][i]/=k; RG int len=0;
33     for (RG int i=0;i<=n;++i) if (fabs(a[l][i])>eps) b[++len]=i;
34     for (RG int i=0;i<=m;++i)
35     if (i!=l && fabs(a[i][e])>eps){
36         k=a[i][e],a[i][e]=0;
37         for (RG int j=1;j<=len;++j) a[i][b[j]]-=k*a[l][b[j]];
38     }
39     return;
40 }
41 
42 il double simplex(){
43     while (1){
44     RG int l,e; for (e=1;e<=n;++e) if (a[0][e]>eps) break;
45     if (e==n+1) return -a[0][0]; RG double tmp=inf;
46     for (RG int i=1;i<=m;++i)
47         if (a[i][e]>eps && a[i][0]/a[i][e]<tmp) tmp=a[i][0]/a[i][e],l=i;
48     if (tmp==inf) return inf; pivot(l,e);
49     }
50 }
51 
52 il void work(){
53     n=gi(),m=gi(); RG int s,t,d;
54     for (RG int i=1;i<=n;++i) scanf("%lf",&a[0][i]);
55     for (RG int i=1;i<=m;++i){
56     s=gi(),t=gi(),d=gi(),a[i][0]=d;
57     for (RG int j=s;j<=t;++j) a[i][j]=1;
58     }
59     printf("%d",(int)(simplex()+0.5)); return;
60 }
61 
62 int main(){
63     File("volunteer+");
64     work();
65     return 0;
66 }

 

posted @ 2017-02-24 12:55  wfj_2048  阅读(336)  评论(0编辑  收藏  举报