zoukankan      html  css  js  c++  java
  • 初识费用流 模板(spfa+slf优化) 餐巾计划问题

      今天学习了最小费用最大流,是网络流算法之一。可以对于一个每条边有一个容量和一个费用(即每单位流的消耗)的图指定一个源点和汇点,求在从源点到汇点的流量最大的前提下的最小费用。

      这里讲一种最基础也是最好掌握的实现算法,就是spfa求费用流。

      其实也很简单,在最大流的基础上,我们将dfs增广替换成对于费用为权值的边跑spfa得到的最短路,相当于一个贪心的思想。证明有一定难度,稍微口糊一下,就像ford-fulkerson一样,这个算法每次都能找到一条单位流费用和最小的路径,又由于路径中每条边的流量相等,每次增广就能使得单位流量的平均费用更小,而最大流流量是不变的,这样就能使得费用最小。

      网络流算法主要考建图,所以模板只要会默下来就够了,还有就是:

      一定要会写spfa!!!

      对于spfa,这里有两个小优化。

      一个是slf优化,就是对于spfa的进队操作,进之前判一下若小于队头就直接插在队头,这个还是蛮有用的,可以提升点速度;

      另一个是记路径的时候可以不用记前趋点,只要记边,反向边指向的就是前趋,就不用多开一个数组的空间。(这个是zxyer学长当时说的)

      下面贴上代码:

      这是网络流24题中的餐巾计划问题。

      题目大意是:一个饭馆每天需要使用ri条干净的餐巾,用完就脏了,干净餐巾可以由3种方式得到:

        1.直接购买,p元一条;

        2.快洗,需要t1天,花费w1元;

        3.慢洗,需要t2天,花费w2元;

      所以我们建6种边,把每天拆成两个点,分别为干净的(1~n)和脏的(n+1~n*2),这里要注意的是,干净的不直接向脏的连边,而是连向T,相当于必须送满ri条给顾客使用,再从S送到脏的一边。

     1 #include<cstdio>
     2 using namespace std;
     3 const int inf=2147483647;
     4 int n,m,p,t1,w1,t2,w2,tot=1,mx,q[2010],d[2010],pree[2010],h[2010];
     5 struct edge{int to,nxt,cst,cap;}e[10010];
     6 bool vis[2010];
     7 int mn(int x,int y){return x>y?y:x;}
     8 void add(int fr,int to,int cst,int cap)
     9 {
    10     e[++tot]={to,h[fr],cst,cap};h[fr]=tot;
    11     e[++tot]={fr,h[to],-cst,0};h[to]=tot;//建一条容量为0费用为-cap的反向边且满足正反边异或值为1方便将边取反 
    12 }
    13 void init()
    14 {
    15     scanf("%d%d%d%d%d%d",&n,&p,&t1,&w1,&t2,&w2);
    16     for(int i=1;i<=n;i++)
    17     {
    18         int r;scanf("%d",&r);mx+=r;
    19         add(0,n+i,p,inf);//直接购买花费p元 
    20         add(0,i,0,r);//当天用完的旧餐巾 
    21         add(i+n,n*2+1,0,r);//当天需要使用的新餐巾 
    22         if(i+t1<=n)add(i,i+n+t1,w1,inf);//快洗花费t1时间,每条w1元 
    23         if(i+t2<=n)add(i,i+n+t2,w2,inf);//快洗花费t2时间,每条w2元 
    24         if(i<n)add(i,i+1,0,inf);//不需要做任何事的餐巾积到明天(也可以先洗完然后放在那边等就是n+i连向n+i+1) 
    25     }
    26     n=n*2+1;
    27 }
    28 bool spfa()
    29 {
    30     for(int i=1;i<=n;i++)d[i]=inf;
    31     int l=0,r=1;q[1]=0;vis[0]=1;
    32     while(l!=r)
    33     {
    34         int x=q[l=l==n?0:l+1];
    35         for(int i=h[x];i;i=e[i].nxt)
    36             if(e[i].cap&&e[i].cst+d[x]<d[e[i].to])
    37             {
    38                 int v=e[i].to;
    39                 pree[v]=i;
    40                 d[v]=d[x]+e[i].cst;
    41                 if(!vis[v])
    42                 {
    43                     if(d[v]>d[l+1])q[r=r==n?0:r+1]=v;
    44                     else q[l]=v,l=l==0?n:l-1;//这边加的是slf优化,用了循环队列来写 
    45                     vis[v]=1;
    46                 }
    47             }
    48         vis[x]=0;
    49     }
    50     return d[n]==inf?0:1;
    51 }
    52 int costflow()
    53 {
    54     int cost=0,mm=0;
    55     while(spfa())
    56     {
    57         int mi=inf;
    58         for(int i=n;i;i=e[pree[i]^1].to)//记路径时记下前趋路径 
    59             mi=mn(mi,e[pree[i]].cap);
    60         for(int i=n;i;i=e[pree[i]^1].to)
    61         {
    62             int ee=pree[i];
    63             e[ee].cap-=mi;
    64             e[ee^1].cap+=mi;
    65         }
    66         cost+=d[n]*mi;
    67         mm+=mi;
    68     }
    69     return mm==mx?cost:0;//是否满流的判断(这道题不会出现不满流的情况,所以不加也可) 
    70 }
    71 int main()
    72 {
    73     init();
    74     printf("%d",costflow());
    75     return 0;
    76 }

      

    本文由qrc出品,若不在本博客上看到,请与本人联系。 网址:http://www.cnblogs.com/qrcer
  • 相关阅读:
    读书笔记之:高级Linux编程(ch14)
    读书笔记之:C++编程惯用法——高级程序员常用的方法和技巧
    读书笔记之:sed与awk
    读书笔记之:Linux——命令、编辑器与shell编程
    读书笔记之:C++必知必会
    读书笔记之:Linux程序设计(第4版)(ch17)
    读书笔记之:Linux管理员指南与Linux系统一本通
    读书笔记之:C++语言的设计与演化(2002)
    读书笔记之:Linux一站式学习
    读书笔记之:GNU/Linux编程指南
  • 原文地址:https://www.cnblogs.com/qrcer/p/6582783.html
Copyright © 2011-2022 走看看