不得不说这题真是思路清奇,真是网络流的一道好题,完全没想到网络流的建图还可以这么建
我们把每一个点拆成两个点,分别表示白天和晚上,白天可以得到干净的餐巾(购买的,慢洗的,快洗的),晚上可以得到脏餐巾(之前剩下的,今天用过的)
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 }