题意:n天,每天需要a[i]个毛巾,每个毛巾用完后需洗净后才能用。有2种清洗方式:A方式需要清洗a天,费用为fa;B方式需要清洗b天,费用为fb。也可以买新的毛巾,费用为f。当天使用后的毛巾可以以后再清洗。求在满足n天的需要的前提下,最少的费用是多少
费用流经典建图,把每一天拆成两个点i 和 i’,用i表示该天需要清洗的毛巾,i'表示该天可以使用的毛巾
建图:
S -> i 流量为a[i],费用为0 表示第i天最多产生a[i]个需要清洗的毛巾
i'->T' 流量为a[i],费用为0 表示第i天需要a[i]个干净的毛巾
S ->i' 流量为正无穷,费用为买新毛巾的费用f 表示第i天可以买任意数量新毛巾,每条新毛巾费用为f
i -> (i+1) 流量为正无穷费用为0 表示把第i天的需要清洗的旧毛巾留到下一天(延后清洗)
i -> (i+a+1)' 流量为正无穷费用为fa 表示第i天可以把任意数量的毛巾用A方式清洗,第(i+a+1)天使用
i -> (i+b+1)' 流量为正无穷费用为fb 表示第i天可以把任意数量的毛巾用B方式清洗,第(i+b+1)天使用
bzoj 1221
var
n,a,b,fa,fb,l,f :longint;
ans :int64;
ss,st :longint;
i :longint;
aa :array[0..1010] of longint;
father :array[0..2010] of longint;
vis :array[0..2010] of boolean;
last,dis,que :array[0..2010] of longint;
pre,other,len :array[0..13010] of longint;
cost :array[0..13010] of longint;
function min(a,b:longint):Longint;
begin
if atl) do
begin
h:=h mod 2005+1;
cur:=que[h];
vis[cur]:=false;
q:=last[cur];
while (q<>0) do
begin
p:=other[q];
if (dis[p]>dis[cur]+cost[q]) and (len[q]>0) then
begin
dis[p]:=dis[cur]+cost[q];
father[p]:=q;
if not vis[p] then
begin
tl:=tl mod 2005+1;
que[tl]:=p;
vis[p]:=true;
end;
end;
q:=pre[q];
end;
end;
if dis[st]=dis[0] then exit(false) else exit(true);
end;
procedure update;
var
tt,cur:longint;
begin
tt:=maxlongint div 10; cur:=st;
while (cur<>ss) do
begin
tt:=min(tt,len[father[cur]]);
cur:=other[father[cur] xor 1];
end;
cur:=st;
while (cur<>ss) do
begin
dec(len[father[cur]],tt);
inc(len[father[cur] xor 1],tt);
inc(ans,int64(tt)*int64(cost[father[cur]]));
cur:=other[father[cur] xor 1];
end;
end;
begin
read(n,a,b,f,fa,fb);
for i:=1 to n do read(aa[i]);
l:=1; ss:=2*n+1; st:=ss+1;
for i:=1 to n do
begin
connect(ss,2*i-1,aa[i],0);
connect(2*i-1,ss,0,0);
connect(2*i,st,aa[i],0);
connect(st,2*i,0,0);
connect(ss,2*i,maxlongint div 10,f);
connect(2*i,ss,0,-f);
end;
for i:=1 to n-1 do
begin
connect(2*i-1,2*i+1,maxlongint div 10,0);
connect(2*i+1,2*i-1,0,0);
end;
for i:=1 to n do
begin
if (i+a+1<=n) then
begin
connect(2*i-1,2*(i+a+1),maxlongint div 10,fa);
connect(2*(i+a+1),2*i-1,0,-fa);
end;
if (i+b+1<=n) then
begin
connect(2*i-1,2*(i+b+1),maxlongint div 10,fb);
connect(2*(i+b+1),2*i-1,0,-fb);
end;
end;
ans:=0;
while spfa do update;
writeln(ans);
end.
此题与上一题相当类似,只有三处略有区别:
(1)新增了m所大学的条件,j表示第j所大学:
S -> j 流量为l[j]费用为0 表示第j所大学最多提供l[j]个人
j -> i' 流量为l[j]费用为p[j] 表示第j所大学为第i天最多提供l[j]个人,每个人费用为p[j]
(2)不能用的变成可以用的方式增多,k表示第k中方式
i -> (i+d[k]+1)' 流量为正无穷费用为q[k] 表示用第k种方式,第(i+d[k]+1)天可以使用
(3)判断是否有解即判断是否满流
var
n,m,hosp,ss,st,l:longint;
ans :int64;
sum,tsum,t :longint;
i,j :longint;
a,b,c,d,e :array[0..105] of longint;
last,que,dis :array[0..310] of longint;
father :array[0..310] of longint;
pre,other,len :array[0..11010] of longint;
cost :array[0..11010] of longint;
vis :array[0..310] of boolean;
function min(a,b:longint):Longint; inline;
begin
if atl) do
begin
h:=h mod 305+1;
cur:=que[h];
vis[cur]:=false;
q:=last[cur];
while (q<>0) do
begin
p:=other[q];
if (dis[p]>dis[cur]+cost[q]) and (len[q]>0) then
begin
dis[p]:=dis[cur]+cost[q];
father[p]:=q;
if not vis[p] then
begin
tl:=tl mod 305+1;
que[tl]:=p;
vis[p]:=true;
end;
end;
q:=pre[q];
end;
end;
if dis[st]=dis[0] then exit(false) else exit(true);
end;
procedure update;
var
tt,cur:longint;
begin
tt:=maxlongint div 10; cur:=st;
while (cur<>ss) do
begin
tt:=min(tt,len[father[cur]]);
cur:=other[father[cur] xor 1];
end;
cur:=st; inc(tsum,tt);
while (cur<>ss) do
begin
dec(len[father[cur]],tt);
inc(len[father[cur] xor 1],tt);
inc(ans,int64(tt)*int64(cost[father[cur]]));
cur:=other[father[cur] xor 1];
end;
end;
procedure build;
var
i,j:longint;
begin
for i:=1 to n do
begin
connect(ss,2*i-1,a[i],0);
connect(2*i-1,ss,0,0);
connect(2*i,st,a[i],0);
connect(st,2*i,0,0);
end;
for i:=1 to m do
begin
connect(ss,2*n+i,c[i],0);
connect(2*n+i,ss,0,0);
end;
for i:=1 to m do
for j:=1 to n do
begin
connect(2*n+i,2*j,c[i],d[i]);
connect(2*j,2*n+i,0,-d[i]);
end;
for i:=1 to n-1 do
begin
connect(2*i-1,2*i+1,maxlongint div 10,0);
connect(2*i+1,2*i-1,0,0);
end;
for i:=1 to n do
for j:=1 to hosp do
if i+b[j]+1<=n then
begin
connect(2*i-1,2*(i+b[j]+1),maxlongint div 10,e[j]);
connect(2*(i+b[j]+1),2*i-1,0,-e[j]);
end;
end;
begin
read(t);
for j:=1 to t do
begin
l:=1; ans:=0; sum:=0; tsum:=0;
fillchar(last,sizeof(last),0);
fillchar(vis,sizeof(vis),false);
read(n,m,hosp);
ss:=2*n+m+1; st:=ss+1;
for i:=1 to n do read(a[i]);
for i:=1 to m do read(c[i],d[i]);
for i:=1 to hosp do read(b[i],e[i]);
for i:=1 to n do inc(sum,a[i]);
build;
while spfa do update;
if tsum=sum then writeln('Case ',j,': ',ans)
else writeln('Case ',j,': impossible');
end;
end.
——by Eirlys