[网络流24题] 餐巾计划问题 (费用流)

洛谷传送门 LOJ传送门

这估计是网络流$24$题图最难建的一个了..刚了2h没刚出来

首先,肯定要把每一天拆成$2$个点,表示白天和晚上

正常的思路一般是:源点和白天连,白天和晚上连边,晚上和汇点连,晚上再分别向快洗慢洗对应的早上连边,白天和白天之间连边,然后跑费用流..

然而我们发现怎么跑都不行啊!为啥快洗慢洗的边都走不了了呢???

因为快洗慢洗会减少流量,不满足费用流:最小费用最大流的定义

所以我们要改一下建图方式,拆点的思路不变:

1.源点$s$向晚上连边,流量为$a_{i}$,费用为$0$,表示这一天晚上获得了$a_{i}$个旧餐巾

2.白天向汇点$t$连边,流量为$a_{i}$,费用为$0$,表示这一天白天需要$a_{i}$个新餐巾

3.第$i$天白天和向$i+1$白天连边,流量为$inf$,费用为$0$,表示今天白天的新餐巾可以留到明天白天

4.第$i$天晚上向第$i+$慢洗天数/$i+$快洗天数的白天连边,流量为$inf$,费用为慢洗费用/快洗费用,表示把旧餐巾送洗

5.源点$s$向白天连边,流量为$inf$,费用为p,表示白天还可以直接购买新餐巾

然后跑最小费用最大流即可

————————————————————分割线-————————————————————————————

2020.7.17  upd

发现自己以前写的题解有点问题,其实应该是旧点间和新旧点间都连,而不是新点间,但结果是一样的

此外,1是由于让新点向旧点连边是无用的,流直接从新点流向T了。

第i天必然消耗ri个新餐巾,产生ri个旧餐巾

所以S直接向旧点连流量为ri,费用为0的边,把同一天的新旧点彻底拆开

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N1 4010
 5 #define M1 25010
 6 #define ll long long
 7 #define dd double
 8 #define inf 0x3f3f3f3f
 9 using namespace std;
10 
11 int gint()
12 {
13     int ret=0,fh=1;char c=getchar();
14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
16     return ret*fh;
17 }
18 struct Edge{
19 int head[N1],to[M1<<1],nxt[M1<<1],flow[M1<<1],cost[M1<<1],cte;
20 void ae(int u,int v,int F,int C)
21 {
22     cte++; to[cte]=v; flow[cte]=F; cost[cte]=C;
23     nxt[cte]=head[u]; head[u]=cte;
24 }
25 }e,E;
26 int n,nn,S,T,p,A,B,C,D;
27 int a[N1],que[M1<<1],hd,tl,cost[N1],flow[N1],use[N1],id[N1],de;
28 int spfa()
29 {
30     int x,j,v;
31     memset(cost,0x3f,sizeof(cost)); memset(flow,0,sizeof(flow)); 
32     hd=1,tl=0; que[++tl]=S; cost[S]=0; flow[S]=inf; use[S]=1;
33     while(hd<=tl)
34     {
35         x=que[hd++];
36         for(j=e.head[x];j;j=e.nxt[j])
37         {
38             v=e.to[j]; 
39             if( cost[v]>cost[x]+e.cost[j] && e.flow[j]>0 )
40             {
41                 cost[v]=cost[x]+e.cost[j]; id[v]=j; 
42                 flow[v]=min(flow[x],e.flow[j]);
43                 if(!use[v]) que[++tl]=v, use[v]=1;
44             }
45         }
46         use[x]=0;
47     }
48     de++;
49     return cost[T]!=inf;
50 }
51 ll EK()
52 {
53     int x,fl;ll tcost=0;
54     while(spfa())
55     {
56         fl=flow[T]; tcost+=1ll*fl*cost[T]; 
57         for(x=T;x!=S;x=e.to[id[x]^1])
58         {
59             e.flow[id[x]]-=fl;
60             e.flow[id[x]^1]+=fl;
61         }
62     }
63     return tcost;
64 }
65 
66 int main()
67 {
68     scanf("%d",&n); nn=2*n;
69     int i,j; S=0; T=nn+1; e.cte=1;
70     for(i=1;i<=n;i++) a[i]=gint();
71     p=gint(); A=gint(); B=gint(); C=gint(); D=gint();
72     for(i=1;i<=n;i++) e.ae(i+n,T,a[i],0), e.ae(T,i+n,0,0);
73     for(i=1;i<=n;i++) e.ae(S,i,a[i],0), e.ae(i,S,0,0);
74     for(i=1;i<=n;i++) e.ae(S,i+n,inf,p), e.ae(i+n,S,0,-p);
75     for(i=n+1;i<n+n;i++) e.ae(i,i+1,inf,0), e.ae(i+1,i,0,0); 
76     for(i=1;i+A<=n;i++) e.ae(i,i+A+n,inf,B), e.ae(i+A+n,i,0,-B);
77     for(i=1;i+C<=n;i++) e.ae(i,i+C+n,inf,D), e.ae(i+C+n,i,0,-D);
78     printf("%lld\n",EK());
79     return 0;
80 }

 

posted @ 2019-01-20 10:54  guapisolo  阅读(115)  评论(0编辑  收藏  举报