题意:传送门
题解:这个题需要使用拆点,因为这一天毛巾用完后可以留到下一天,或者用于A洗衣,或者用于B洗衣中,同时每一天的来源也有买的或者洗好的,所以拆为两个,一边放置用过的毛巾(1-n),一边放需要用的毛巾(n+1,n+n),首先将S与需要用的毛巾连(S,(n+i),inf,p),然后再将需要用的毛巾与最后的T连边(n+i,T,r[i],0),将源点与所需要的毛巾连边(S,i,r[i],0),然后每天使用的毛巾可以留到下一天使用,同时也可以用于两种洗衣方法,那么连边(i,i+1,inf,0),(i,n+i+m1,inf,c1),(i,n+i+m2,inf,c2)之后跑出最小费用最大流即可。
附上代码:
#include<iostream>
#include<cstdio>
#define inf 0x7fffffff
#define T 4000005
using namespace std;
int cnt=1,day,p,m,f,n,s,ans;
int from[4000005],q[4000005],dis[4000005],head[4000005];
bool inq[4000005];
struct data{int from,to,next,v,c;}e[2000001];
void ins(int u,int v,int w,int c)
{
cnt++;
e[cnt].from=u;e[cnt].to=v;
e[cnt].v=w;e[cnt].c=c;
e[cnt].next=head[u];head[u]=cnt;
}
void insert(int u,int v,int w,int c)
{ins(u,v,w,c);ins(v,u,0,-c);}
bool spfa()
{
for(int i=0;i<=T;i++)dis[i]=inf;
int t=0,w=1,i,now;
dis[0]=q[0]=0;inq[0]=1;
while(t!=w)
{
now=q[t];t++;if(t==2001)t=0;
for(int i=head[now];i;i=e[i].next)
{
if(e[i].v&&dis[e[i].to]>dis[now]+e[i].c)
{
from[e[i].to]=i;
dis[e[i].to]=dis[now]+e[i].c;
if(!inq[e[i].to])
{
inq[e[i].to]=1;
q[w++]=e[i].to;
if(w==2001)w=0;
}
}
}
inq[now]=0;
}
if(dis[T]==inf)return 0;return 1;
}
void mcf()
{
int i,x=inf;
i=from[T];
while(i)
{
x=min(e[i].v,x);
i=from[e[i].from];
}
i=from[T];
while(i)
{
e[i].v-=x;
e[i^1].v+=x;
ans+=x*e[i].c;
i=from[e[i].from];
}
}
int main()
{
scanf("%d%d%d%d%d%d",&day,&m,&n,&f,&s,&p);
int x;
for(int i=1;i<=day;i++)
{
if(i+1<=day)insert(i,i+1,inf,0);
if(i+m+1<=day)insert(i,day+i+m+1,inf,f);
if(i+n+1<=day)insert(i,day+i+n+1,inf,s);
insert(0,day+i,inf,p);
scanf("%d",&x);
insert(0,i,x,0);
insert(day+i,T,x,0);
}
while(spfa())mcf();
printf("%d",ans);
return 0;
}