题意:一个餐厅在相继的 N 天里,每天需用的餐巾数不尽相同。
假设第 i 天需要 ri块餐巾(i=1,2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 p 分;
或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;
或者送到慢洗部,洗一块需 n 天(n>m),其费用为 s<f 分。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。
但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 N 天中餐巾使用计划,使总的花费最小。
编程找出一个最佳餐巾使用计划。
思路:RYZ作业
费用流经典模型之一
BYVOID:
【建模方法】
把每天分为二分图两个集合中的顶点Xi,Yi,建立附加源S汇T。
1、从S向每个Xi连一条容量为ri,费用为0的有向边。
2、从每个Yi向T连一条容量为ri,费用为0的有向边。
3、从S向每个Yi连一条容量为无穷大,费用为p的有向边。
4、从每个Xi向Xi+1(i+1<=N)连一条容量为无穷大,费用为0的有向边。
5、从每个Xi向Yi+m(i+m<=N)连一条容量为无穷大,费用为f的有向边。
6、从每个Xi向Yi+n(i+n<=N)连一条容量为无穷大,费用为s的有向边。
求网络最小费用最大流,费用流值就是要求的最小总花费。
【建模分析】
这个问题的主要约束条件是每天的餐巾够用,而餐巾的来源可能是最新购买,也可能是前几天送洗,
今天刚刚洗好的餐巾。每天用完的餐巾可以选择送到快洗部或慢洗部,或者留到下一天再处理。
经过分析可以把每天要用的和用完的分离开处理,建模后就是二分图。
二分图X集合中顶点Xi表示第i天用完的餐巾,其数量为ri,所以从S向Xi连接容量为ri的边作为限制。
Y集合中每个点Yi则是第i天需要的餐巾,数量为ri,与T连接的边容量作为限制。
每天用完的餐巾可以选择留到下一天(Xi->Xi+1),不需要花费,
送到快洗部(Xi->Yi+m),费用为f,
送到慢洗部(Xi->Yi+n),费用为s。
每天需要的餐巾除了刚刚洗好的餐巾,还可能是新购买的(S->Yi),费用为p。
在网络上求出的最小费用最大流,满足了问题的约束条件(因为在这个图上最大流一定可以使与T连接的边全部满流,其他边只要有可行流就满足条件),
而且还可以保证总费用最小,就是我们的优化目标。
UPD(19.10.29):Xi之间和Yi之间没有边是因为他们分别和S,T连接的边已经等价于每天使用ri了
var head,vet,next,len1,len2,fan:array[..]of longint;
inq:array[..]of boolean;
q:array[..]of longint;
num,pre:array[..,..]of longint;
dis,a:array[..]of longint;
n,m,p1,m1,f1,n1,s1,ans,source,src,s,tot,i,j:longint; function min(x,y:longint):longint;
begin
if x<y then exit(x);
exit(y);
end; procedure add(a,b,c,d:longint);
begin
inc(tot);
next[tot]:=head[a];
vet[tot]:=b;
len1[tot]:=c;
len2[tot]:=d;
head[a]:=tot; inc(tot);
next[tot]:=head[b];
vet[tot]:=a;
len1[tot]:=;
len2[tot]:=-d;
head[b]:=tot;
end; function spfa:boolean;
var u,e,v,i,t,w:longint;
begin
for i:= to s do
begin
dis[i]:=maxlongint>>;
inq[i]:=false;
end;
t:=; w:=; q[]:=source; dis[source]:=; inq[source]:=true;
while t<w do
begin
inc(t); u:=q[t mod ]; inq[u]:=false;
e:=head[u];
while e<> do
begin
v:=vet[e];
if (len1[e]>)and(dis[u]+len2[e]<dis[v]) then
begin
dis[v]:=dis[u]+len2[e];
pre[v,]:=u;
pre[v,]:=e;
if not inq[v] then
begin
inc(w); q[w mod ]:=v; inq[v]:=true;
end;
end;
e:=next[e];
end;
end;
if dis[src]=maxlongint>> then exit(false);
exit(true);
end; procedure mcf;
var k,e,t:longint;
begin
k:=src; t:=maxlongint;
while k<>source do
begin
t:=min(t,len1[pre[k,]]);
k:=pre[k,];
end;
k:=src;
while k<>source do
begin
e:=pre[k,];
len1[e]:=len1[e]-t;
len1[fan[e]]:=len1[fan[e]]+t;
ans:=ans+t*len2[e];
k:=pre[k,];
end;
end; begin
assign(input,'codevs1237.in'); reset(input);
assign(output,'codevs1237.out'); rewrite(output);
readln(n,p1,m1,f1,n1,s1);
for i:= to do
if i and = then fan[i]:=i+
else fan[i]:=i-;
for i:= to n do read(a[i]);
for i:= to n do
for j:= to do
begin
inc(s); num[i,j]:=s;
end;
source:=s+; src:=s+; s:=s+;
for i:= to n do add(source,num[i,],a[i],);
for i:= to n do add(num[i,],src,a[i],);
for i:= to n- do add(num[i,],num[i+,],maxlongint,);
for i:= to n do add(source,num[i,],maxlongint,p1);
for i:= to n do
if i+m1<=n then add(num[i,],num[i+m1,],maxlongint,f1);
for i:= to n do
if i+n1<=n then add(num[i,],num[i+n1,],maxlongint,s1);
while spfa do mcf;
writeln(ans);
close(input);
close(output);
end.