bzoj 1221 && bzoj 3280 费用流【餐巾计划】

10 篇文章 0 订阅

题意: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 a<b then exit(a) else exit(b);
end;

procedure connect(x,y,z,c:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
   cost[l]:=c;
end;

function spfa:boolean;
var
        h,tl,p,q,cur:longint;
begin
   fillchar(dis,sizeof(dis),127);
   h:=0; tl:=1; que[1]:=ss; dis[ss]:=0;
   while (h<>tl) 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.


bzoj 3280 

此题与上一题相当类似,只有三处略有区别:

(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 a<b then exit(a) else exit(b);
end;

procedure connect(x,y,z,c:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
   cost[l]:=c;
end;

function spfa:boolean;
var
        h,tl,p,q,cur:longint;
begin
   fillchar(dis,sizeof(dis),127);
   h:=0; tl:=1; que[1]:=ss; dis[ss]:=0;
   while (h<>tl) 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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值