洛谷P1251 餐巾计划问题(费用流)

传送门

不得不说这题真是思路清奇,真是网络流的一道好题,完全没想到网络流的建图还可以这么建

我们把每一个点拆成两个点,分别表示白天和晚上,白天可以得到干净的餐巾(购买的,慢洗的,快洗的),晚上可以得到脏餐巾(之前剩下的,今天用过的)

1.每一天,我们都从源点向晚上连边,容量为餐巾,费用$0$,表示可以免费获得这么多餐巾,从早上想汇点连边,容量为餐巾,费用为$0$,表示可以免费提供这么多餐巾,流满时表示当天餐巾够用

2.从每一天晚上向第二天晚上连边,容量$inf$,费用$0$,表示可以把脏餐巾留到第二天晚上(因为有可能有的餐巾不用洗第二天就已经够了)

3.在每一天晚上向这一天的$t1$天之后的早上连容量为$inf$,费用快洗钱数的边,表示每天晚上可以快洗之后为$t1$天之后送去干净的餐巾

4.在每一天晚上向这一天的$t2$天之后的早上连容量为$inf$,费用慢洗钱数的边,表示每天晚上可以慢洗之后为$t2$天之后送去干净的餐巾

5.从起点向每一天早上连容量$inf$,费用为买餐巾费用的边,表示每天早上都可以买餐巾

ps:后四点建边时要判断是否合法

然后为了让每天早上餐巾都够用,得让网络流满,所以跑一个最小费用最大流即可

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<cstring>
 6 #define inf 0x3f3f3f3f
 7 #define ll long long
 8 using namespace std;
 9 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
10 char buf[1<<21],*p1=buf,*p2=buf;
11 inline int read(){
12     #define num ch-'0'
13     char ch;bool flag=0;int res;
14     while(!isdigit(ch=getc()))
15     (ch=='-')&&(flag=true);
16     for(res=num;isdigit(ch=getc());res=res*10+num);
17     (flag)&&(res=-res);
18     #undef num
19     return res;
20 }
21 const int N=4005,M=50005;
22 int ver[M],Next[M],head[N],edge[M],flow[M],tot=1;
23 int disf[N],vis[N],Pre[N],last[N];ll dis[N];
24 int n,m,t1,t2,m1,m2,s,t;
25 queue<int> q;
26 inline void add(int u,int v,int f,int e){
27     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,flow[tot]=f,edge[tot]=e;
28     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,flow[tot]=0,edge[tot]=-e;
29 }
30 bool spfa(){
31     memset(dis,0x3f,sizeof(dis));
32     q.push(s),dis[s]=0,disf[s]=inf,Pre[t]=-1;
33     while(!q.empty()){
34         int u=q.front();q.pop();vis[u]=0;
35         for(int i=head[u];i;i=Next[i]){
36             int v=ver[i];
37             if(flow[i]&&dis[v]>dis[u]+edge[i]){
38                 dis[v]=dis[u]+edge[i],last[v]=i,Pre[v]=u;
39                 disf[v]=min(disf[u],flow[i]);
40                 if(!vis[v]) vis[v]=1,q.push(v);
41             }
42         }
43     }
44     return ~Pre[t];
45 }
46 ll dinic(){
47     ll mincost=0;
48     while(spfa()){
49         int u=t;mincost+=disf[t]*dis[t];
50         while(u!=s){
51             flow[last[u]]-=disf[t];
52             flow[last[u]^1]+=disf[t];
53             u=Pre[u];
54         }
55     }
56     return mincost;
57 }
58 int main(){
59     n=read();
60     s=0,t=2*n+1;
61     for(int i=1;i<=n;++i){
62         int x=read();
63         add(s,i,x,0),add(i+n,t,x,0);
64     }
65     m=read(),t1=read(),m1=read(),t2=read(),m2=read();
66     for(int i=1;i<=n;++i){
67         if(i+1<=n) add(i,i+1,inf,0);
68         if(i+t1<=n) add(i,i+n+t1,inf,m1);
69         if(i+t2<=n) add(i,i+n+t2,inf,m2);
70         add(s,i+n,inf,m);
71     }
72     printf("%lld
",dinic());
73     return 0;
74 }
原文地址:https://www.cnblogs.com/bztMinamoto/p/9507521.html