不得不说这题真是思路清奇,真是网络流的一道好题,完全没想到网络流的建图还可以这么建
我们把每一个点拆成两个点,分别表示白天和晚上,白天可以得到干净的餐巾(购买的,慢洗的,快洗的),晚上可以得到脏餐巾(之前剩下的,今天用过的)
1.每一天,我们都从源点向晚上连边,容量为餐巾,费用$0$,表示可以免费获得这么多餐巾,从早上想汇点连边,容量为餐巾,费用为$0$,表示可以免费提供这么多餐巾,流满时表示当天餐巾够用
2.从每一天晚上向第二天晚上连边,容量$inf$,费用$0$,表示可以把脏餐巾留到第二天晚上(因为有可能有的餐巾不用洗第二天就已经够了)
3.在每一天晚上向这一天的$t1$天之后的早上连容量为$inf$,费用快洗钱数的边,表示每天晚上可以快洗之后为$t1$天之后送去干净的餐巾
4.在每一天晚上向这一天的$t2$天之后的早上连容量为$inf$,费用慢洗钱数的边,表示每天晚上可以慢洗之后为$t2$天之后送去干净的餐巾
5.从起点向每一天早上连容量$inf$,费用为买餐巾费用的边,表示每天早上都可以买餐巾
ps:后四点建边时要判断是否合法
然后为了让每天早上餐巾都够用,得让网络流满,所以跑一个最小费用最大流即可
1 //minamoto 2 #include3 #include 4 #include 5 #include 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 num19 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 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\n",dinic());73 return 0;74 }