zoukankan      html  css  js  c++  java
  • 费用流(自用,勿看)

    注意:这是一篇个人学习笔记,如果有人因为某些原因点了进来并且要看一下,请一定谨慎地阅读,因为可能存在各种奇怪的错误,如果有人发现错误请指出谢谢!


    最小费用最大流

    https://www.luogu.org/problemnew/show/P3381

    注意:以下算法不能处理费用带负环的图(如果我没搞错的话,应该只要原图(不考虑退流用的反向边)没有负环就行);下面zkw博客里有关于负环的处理方法,先咕了

    spfa费用流

    就是每一条边多一个属性:单位流量的费用

    方法是在从0流开始找最大流过程中,保证每一次增广都沿着可行且费用最小的路增广,直到不能增广;最大流只有一个,因此最终一定能得到

    就是把EK的bfs换成按费用为边权的spfa。。。

    复杂度的话...EK的增广次数上限的分析好像还是适用的(应该吧?),所以是n^2*m^2

    注意:

    spfa不能在搜到T(u==T)时break

    某一条正向边有cost的费用,对应反向边要加上-cost的费用

    如果要卡常什么的可以加spfa的两个优化(不过好像复杂度会假掉?然而一般的图上都是快的)

    //如果改成longlong,注意33行改成sizeof(ll)

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 #include<queue>
     6 using namespace std;
     7 #define fi first
     8 #define se second
     9 #define mp make_pair
    10 #define pb push_back
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 typedef pair<int,int> pii;
    14 namespace F
    15 {
    16 
    17 struct E
    18 {
    19     int to,nxt,d,from,cap,flow;
    20 }e[101000];
    21 int f1[5010],ne=1;
    22 int S,T,n;
    23 bool inq[5010];
    24 int d[5010],pre[5010],minn[5010];
    25 int flow,cost;
    26 const int INF=0x3f3f3f3f;
    27 void solve()
    28 {
    29     int k,u;
    30     flow=cost=0;
    31     while(1)
    32     {
    33         memset(d,0x3f,sizeof(int)*(n+1));
    34         memset(inq,0,sizeof(bool)*(n+1));
    35         queue<int> q;
    36         q.push(S);d[S]=0;pre[S]=0;inq[S]=1;minn[S]=INF;
    37         while(!q.empty())
    38         {
    39             u=q.front();q.pop();
    40             inq[u]=0;
    41             //if(u==T)    break;
    42             for(k=f1[u];k;k=e[k].nxt)
    43                 if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].to])
    44                 {
    45                     d[e[k].to]=d[u]+e[k].d;
    46                     pre[e[k].to]=k;
    47                     minn[e[k].to]=min(minn[u],e[k].cap-e[k].flow);
    48                     if(!inq[e[k].to])
    49                     {
    50                         inq[e[k].to]=1;
    51                         q.push(e[k].to);
    52                     }
    53                 }
    54         }
    55         if(d[T]==INF)    break;
    56         flow+=minn[T];cost+=d[T]*minn[T];
    57         for(k=pre[T];k;k=pre[e[k].from])
    58         {
    59             e[k].flow+=minn[T];e[k^1].flow-=minn[T];
    60         }
    61     }
    62 }
    63 void me(int a,int b,int c,int d)
    64 {
    65     e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a;
    66     e[ne].cap=c;e[ne].d=d;
    67     e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b;
    68     e[ne].cap=0;e[ne].d=-d;
    69 }
    70 
    71 }
    72 
    73 int n,m,S,T;
    74 int main()
    75 {
    76     int i,a,b,c,d;
    77     scanf("%d%d%d%d",&n,&m,&S,&T);F::S=S;F::T=T;F::n=n;
    78     for(i=1;i<=m;i++)
    79     {
    80         scanf("%d%d%d%d",&a,&b,&c,&d);
    81         F::me(a,b,c,d);
    82     }
    83     F::solve();
    84     printf("%d %d",F::flow,F::cost);
    85     return 0;
    86 }
    View Code

    Primal-Dual 原始对偶算法(费用流) 

    zkw原文

    先看这个johnson算法介绍:https://blog.csdn.net/howarli/article/details/73824179

    说白了,就是可以证明:可以构造出一种方案,给每个点一个权值h[i],然后使得图中每一条边(a,b)的长度增加h[b]-h[a],最终在新图中随便跑两点(u,v)间的最短路d',设原图中两点间最短路为d,那么d'=d+h[u]-h[v]

    只要构造一种方法,使得每一条边加上这个权值后非负,就能一次spfa后都只跑dijkstra了

    算法的流程是:

    spfa-->更新h[i]-->增广-->dijkstra-->更新h[i]-->增广-->dijkstra...直到某一次spfa/dijkstra发现没有增广路时跳出

    具体方法是:

    每一次最短路,从某一点sx开始向每个点跑(只考虑残量网络中边)(sx为S或T,具体见最后)

    更新h[i]时,将每一个h[i]加上前一次最短路时的d[i](表示起始点sx到i的最短路)

    跑最短路时,查询(u,v,w)的边权时就改成w+h[u]-h[v]

    复杂度是:n*m^2*log

    原因:

    其实是不难的。。却因为理不清思路还有搞复杂了,用了很多时间

    目标就是:证明任意一次跑最短路(除了第一次)时,任意边(u,v,w)满足w+h[u]-h[v]>=0;h的取值只要满足这一个条件即可,并没有其他限制(当初就是因为总是去想h的其他意义,导致证不出来)

    不太好考虑,从最开始考虑

    第一遍spfa+更新:根据最短路的性质,更新完后显然h[u]+w>=h[v],即w+h[u]-h[v]>=0

    第一遍增广:设d[i]为第一遍spfa时sx到i的不带h影响的最短路;在增广中有一些边会变成其反边,设这条反边是(v,u,-w),由于增广只会走最短路上的边,可知d[u]+w=d[v],那么增广后-w+d[v]-d[u]=-(w+d[u]-d[v])=0,因此增广不会产生新负权边

    第二遍最短路:由于此时如果给边权加上h的影响的话,图中并没有负权边,因此可以用dijkstra算法跑出最短路;设d[i]为这一遍做出来的sx到i的带h的影响的最短路;设d'[i]为这一遍的sx到i的不带h的影响的最短路(当然并没有算出来,只是用于证明);根据johnson算法的那个证明,d[i]=d'[i]+h[sx]-h[i]=d'[i]-h[i]

    第二遍更新:每个h[i]加上一个d[i],即加上d'[i]-h[i],因此每个h[i]变成d'[i],即当前图不带h的影响的sx到i的最短路

    可以发现每一次增广之前,h[i]都能成为当前图(不带h的影响的)sx到i的最短路,又可以用来辅助增广路

    可以发现这样的证明对于任意一轮的操作都是生效的(只要上一轮符合),可以当做一个归纳法的证明

    证明参考:https://www.luogu.org/problemnew/solution/P3381

    实现:

    可以用多路增广,当然仍然只能增广在(S,T)最短路上的点;注意到如果不加更多的限制条件,且残量网络中有零费用环,那么会陷入无限递归(而且即使判掉这个,这个“多路增广”我感觉跟爆搜也没什么区别(?猜的,并不清楚)),因此可以干脆写成一次增广每个点最多经过一次(我没有更好的方法);这样子一次可能增广不完全部路径,因此每次要增广的时候不断进行多路增广直到无法增广;(这样的多路增广感觉很不好啊,但是网上要么是单路增广,要么就是这样(?有别的也说不定),也许真的只能这样?)

    同一轮中,每一条增广路的总费用都相等(毕竟都是最短路),等于这一轮时的h[S](或h[T])(毕竟这等于当前图实际上S到T的最短路);这样就可以计算每一次增广增加的费用了

    (sx可以设为S;sx也可以设为T,当然这样的话要对反图跑最短路,听说会快一点,另外前面多路增广时的判断最短路要改一下)

    实现参考:https://panda-2134.blog.luogu.org/solution-p3381

    多路增广版本:

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<vector>
      5 #include<queue>
      6 using namespace std;
      7 #define fi first
      8 #define se second
      9 #define mp make_pair
     10 #define pb push_back
     11 typedef long long ll;
     12 typedef unsigned long long ull;
     13 typedef pair<int,int> pii;
     14 namespace F
     15 {
     16 
     17 struct E
     18 {
     19     int to,nxt,d,from,cap,flow;
     20 }e[101000];
     21 int f1[5010],ne=1;
     22 int S,T,n;
     23 bool inq[5010],*vis=inq;
     24 int d[5010];
     25 int h[5010];
     26 int flow,cost;
     27 bool spfa()
     28 {
     29     int u,k,k1;
     30     memset(d,0x3f,sizeof(d[0])*(n+1));
     31     memset(inq,0,sizeof(inq[0])*(n+1));
     32     queue<int> q;
     33     q.push(T);d[T]=0;inq[T]=1;
     34     while(!q.empty())
     35     {
     36         u=q.front();q.pop();
     37         inq[u]=0;
     38         for(k1=f1[u];k1;k1=e[k1].nxt)
     39         {
     40             k=k1^1;
     41             if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from])
     42             {
     43                 d[e[k].from]=d[u]+e[k].d;
     44                 if(!inq[e[k].from])
     45                 {
     46                     inq[e[k].from]=1;
     47                     q.push(e[k].from);
     48                 }
     49             }
     50         }
     51     }
     52     return d[S]!=0x3f3f3f3f;
     53 }
     54 bool dij()
     55 {
     56     int u,k,k1;pii t;
     57     memset(d,0x3f,sizeof(d[0])*(n+1));
     58     memset(vis,0,sizeof(vis[0])*(n+1));
     59     priority_queue<pii,vector<pii>,greater<pii> > q;
     60     q.push(mp(0,T));d[T]=0;
     61     while(!q.empty())
     62     {
     63         t=q.top();q.pop();
     64         u=t.se;
     65         if(vis[u])    continue;
     66         vis[u]=1;
     67         for(k1=f1[u];k1;k1=e[k1].nxt)
     68         {
     69             k=k1^1;
     70             if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from])
     71             {
     72                 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from];
     73                 q.push(mp(d[e[k].from],e[k].from));
     74             }
     75         }
     76     }
     77     return d[S]!=0x3f3f3f3f;
     78 }
     79 void update()
     80 {
     81     for(int i=1;i<=n;i++)    h[i]+=d[i];
     82 }
     83 int dfs(int u,int x)
     84 {
     85     if(u==T||x==0)    return x;
     86     int flow=0,f;
     87     vis[u]=1;
     88     for(int k=f1[u];k;k=e[k].nxt)
     89         if(!vis[e[k].to]&&e[k].cap>e[k].flow&&h[e[k].to]==h[u]-e[k].d)
     90         {
     91             f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow));
     92             e[k].flow+=f;e[k^1].flow-=f;flow+=f;
     93             if(flow==x)    return flow;
     94         }
     95     return flow;
     96 }
     97 void augment()
     98 {
     99     int f;
    100     while(1)
    101     {
    102         memset(vis,0,sizeof(vis[0])*(n+1));
    103         f=dfs(S,0x3f3f3f3f);
    104         if(!f)    break;
    105         flow+=f;cost+=f*h[S];
    106     }
    107 }
    108 void solve()
    109 {
    110     flow=cost=0;
    111     memset(h,0,sizeof(h[0])*(n+1));
    112     if(!spfa())    return;
    113     do
    114     {
    115         update();
    116         augment();
    117     }while(dij());
    118 }
    119 void me(int a,int b,int c,int d)
    120 {
    121     e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a;
    122     e[ne].cap=c;e[ne].d=d;
    123     e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b;
    124     e[ne].cap=0;e[ne].d=-d;
    125 }
    126 
    127 }
    128 
    129 int n,m,S,T;
    130 int main()
    131 {
    132     int i,a,b,c,d;
    133     scanf("%d%d%d%d",&n,&m,&S,&T);F::S=S;F::T=T;F::n=n;
    134     for(i=1;i<=m;i++)
    135     {
    136         scanf("%d%d%d%d",&a,&b,&c,&d);
    137         F::me(a,b,c,d);
    138     }
    139     F::solve();
    140     printf("%d %d",F::flow,F::cost);
    141     return 0;
    142 }
    View Code

    以上版本改了pbds的堆,跑的很快:

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<vector>
      5 #include<queue>
      6 #include<ext/pb_ds/assoc_container.hpp>
      7 #include<ext/pb_ds/priority_queue.hpp>
      8 using namespace std;
      9 #define fi first
     10 #define se second
     11 #define mp make_pair
     12 #define pb push_back
     13 typedef long long ll;
     14 typedef unsigned long long ull;
     15 typedef pair<int,int> pii;
     16 namespace F
     17 {
     18 
     19 struct E
     20 {
     21     int to,nxt,d,from,cap,flow;
     22 }e[101000];
     23 int f1[5010],ne=1;
     24 int S,T,n;
     25 bool inq[5010],*vis=inq;
     26 int d[5010];
     27 int h[5010];
     28 int flow,cost;
     29 typedef __gnu_pbds::priority_queue<pii,greater<pii> > pq;
     30 pq::point_iterator it[5010];
     31 //const int INF=0x3f3f3f3f;
     32 bool spfa()
     33 {
     34     int u,k,k1;
     35     memset(d,0x3f,sizeof(d[0])*(n+1));
     36     memset(inq,0,sizeof(inq[0])*(n+1));
     37     queue<int> q;
     38     q.push(T);d[T]=0;inq[T]=1;
     39     while(!q.empty())
     40     {
     41         u=q.front();q.pop();
     42         inq[u]=0;
     43         for(k1=f1[u];k1;k1=e[k1].nxt)
     44         {
     45             k=k1^1;
     46             if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from])
     47             {
     48                 d[e[k].from]=d[u]+e[k].d;
     49                 if(!inq[e[k].from])
     50                 {
     51                     inq[e[k].from]=1;
     52                     q.push(e[k].from);
     53                 }
     54             }
     55         }
     56     }
     57     return d[S]!=0x3f3f3f3f;
     58 }
     59 bool dij()
     60 {
     61     int i,u,k,k1;pii t;
     62     memset(d,0x3f,sizeof(d[0])*(n+1));
     63     memset(vis,0,sizeof(vis[0])*(n+1));
     64     pq q;
     65     for(i=1;i<=n;i++)    it[i]=q.end();
     66     it[T]=q.push(mp(0,T));d[T]=0;
     67     while(!q.empty())
     68     {
     69         t=q.top();q.pop();
     70         u=t.se;
     71         if(vis[u])    continue;
     72         vis[u]=1;
     73         for(k1=f1[u];k1;k1=e[k1].nxt)
     74         {
     75             k=k1^1;
     76             if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from])
     77             {
     78                 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from];
     79                 if(it[e[k].from]!=q.end())    q.modify(it[e[k].from],mp(d[e[k].from],e[k].from));
     80                 else    it[e[k].from]=q.push(mp(d[e[k].from],e[k].from));
     81             }
     82         }
     83     }
     84     return d[S]!=0x3f3f3f3f;
     85 }
     86 void update()
     87 {
     88     for(int i=1;i<=n;i++)    h[i]+=d[i];
     89 }
     90 int dfs(int u,int x)
     91 {
     92     if(u==T||x==0)    return x;
     93     int flow=0,f;
     94     vis[u]=1;
     95     for(int k=f1[u];k;k=e[k].nxt)
     96         if(!vis[e[k].to]&&e[k].cap>e[k].flow&&h[e[k].to]==h[u]-e[k].d)
     97         {
     98             f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow));
     99             e[k].flow+=f;e[k^1].flow-=f;flow+=f;
    100             if(flow==x)    return flow;
    101         }
    102     return flow;
    103 }
    104 void augment()
    105 {
    106     int f;
    107     while(1)
    108     {
    109         memset(vis,0,sizeof(vis[0])*(n+1));
    110         f=dfs(S,0x3f3f3f3f);
    111         if(!f)    break;
    112         flow+=f;cost+=f*h[S];
    113     }
    114 }
    115 void solve()
    116 {
    117     flow=cost=0;
    118     memset(h,0,sizeof(h[0])*(n+1));
    119     if(!spfa())    return;
    120     do
    121     {
    122         update();
    123         augment();
    124     }while(dij());
    125 }
    126 void me(int a,int b,int c,int d)
    127 {
    128     e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a;
    129     e[ne].cap=c;e[ne].d=d;
    130     e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b;
    131     e[ne].cap=0;e[ne].d=-d;
    132 }
    133 
    134 }
    135 
    136 int n,m,S,T;
    137 int main()
    138 {
    139     int i,a,b,c,d;
    140     scanf("%d%d%d%d",&n,&m,&S,&T);F::S=S;F::T=T;F::n=n;
    141     for(i=1;i<=m;i++)
    142     {
    143         scanf("%d%d%d%d",&a,&b,&c,&d);
    144         F::me(a,b,c,d);
    145     }
    146     F::solve();
    147     printf("%d %d",F::flow,F::cost);
    148     return 0;
    149 }
    View Code

    单路增广版本:

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<vector>
      5 #include<queue>
      6 #include<ext/pb_ds/assoc_container.hpp>
      7 #include<ext/pb_ds/priority_queue.hpp>
      8 using namespace std;
      9 #define fi first
     10 #define se second
     11 #define mp make_pair
     12 #define pb push_back
     13 typedef long long ll;
     14 typedef unsigned long long ull;
     15 typedef pair<int,int> pii;
     16 namespace F
     17 {
     18 
     19 struct E
     20 {
     21     int to,nxt,d,from,cap,flow;
     22 }e[101000];
     23 int f1[5010],ne=1;
     24 int S,T,n;
     25 bool inq[5010],*vis=inq;
     26 int d[5010];
     27 int h[5010];
     28 int pre[5010];
     29 int flow,cost;
     30 typedef __gnu_pbds::priority_queue<pii,greater<pii> > pq;
     31 pq::point_iterator it[5010];
     32 //const int INF=0x3f3f3f3f;
     33 bool spfa()
     34 {
     35     int u,k,k1;
     36     memset(d,0x3f,sizeof(d[0])*(n+1));
     37     memset(inq,0,sizeof(inq[0])*(n+1));
     38     queue<int> q;
     39     q.push(T);d[T]=0;inq[T]=1;pre[T]=0;
     40     while(!q.empty())
     41     {
     42         u=q.front();q.pop();
     43         inq[u]=0;
     44         for(k1=f1[u];k1;k1=e[k1].nxt)
     45         {
     46             k=k1^1;
     47             if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from])
     48             {
     49                 d[e[k].from]=d[u]+e[k].d;
     50                 pre[e[k].from]=k;
     51                 if(!inq[e[k].from])
     52                 {
     53                     inq[e[k].from]=1;
     54                     q.push(e[k].from);
     55                 }
     56             }
     57         }
     58     }
     59     return d[S]!=0x3f3f3f3f;
     60 }
     61 bool dij()
     62 {
     63     int i,u,k,k1;pii t;
     64     memset(d,0x3f,sizeof(d[0])*(n+1));
     65     memset(vis,0,sizeof(vis[0])*(n+1));
     66     pq q;
     67     for(i=1;i<=n;i++)    it[i]=q.end();
     68     it[T]=q.push(mp(0,T));d[T]=0;pre[T]=0;
     69     while(!q.empty())
     70     {
     71         t=q.top();q.pop();
     72         u=t.se;
     73         if(vis[u])    continue;
     74         vis[u]=1;
     75         for(k1=f1[u];k1;k1=e[k1].nxt)
     76         {
     77             k=k1^1;
     78             if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from])
     79             {
     80                 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from];
     81                 pre[e[k].from]=k;
     82                 if(it[e[k].from]!=q.end())    q.modify(it[e[k].from],mp(d[e[k].from],e[k].from));
     83                 else    it[e[k].from]=q.push(mp(d[e[k].from],e[k].from));
     84             }
     85         }
     86     }
     87     return d[S]!=0x3f3f3f3f;
     88 }
     89 void update()
     90 {
     91     for(int i=1;i<=n;i++)    h[i]+=d[i];
     92 }
     93 void augment()
     94 {
     95     int k,f=0x3f3f3f3f;
     96     for(k=pre[S];k;k=pre[e[k].to])
     97     {
     98         f=min(f,e[k].cap-e[k].flow);
     99     }
    100     for(k=pre[S];k;k=pre[e[k].to])
    101     {
    102         e[k].flow+=f;e[k^1].flow-=f;
    103     }
    104     flow+=f;cost+=f*h[S];
    105 }
    106 void solve()
    107 {
    108     flow=cost=0;
    109     memset(h,0,sizeof(h[0])*(n+1));
    110     if(!spfa())    return;
    111     do
    112     {
    113         update();
    114         augment();
    115     }while(dij());
    116 }
    117 void me(int a,int b,int c,int d)
    118 {
    119     e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a;
    120     e[ne].cap=c;e[ne].d=d;
    121     e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b;
    122     e[ne].cap=0;e[ne].d=-d;
    123 }
    124 
    125 }
    126 
    127 int n,m,S,T;
    128 int main()
    129 {
    130     int i,a,b,c,d;
    131     scanf("%d%d%d%d",&n,&m,&S,&T);F::S=S;F::T=T;F::n=n;
    132     for(i=1;i<=m;i++)
    133     {
    134         scanf("%d%d%d%d",&a,&b,&c,&d);
    135         F::me(a,b,c,d);
    136     }
    137     F::solve();
    138     printf("%d %d",F::flow,F::cost);
    139     return 0;
    140 }
    View Code
  • 相关阅读:
    html部分
    elementUi 新建和编辑dialog-input无法输入的小坑
    js array methods
    css-渐变背景,爱了爱了。
    css-iview官网布局
    10、TypeScript中的装饰器
    常见的预制注解
    javadoc工具
    元注解
    注解的概念
  • 原文地址:https://www.cnblogs.com/hehe54321/p/9623437.html
Copyright © 2011-2022 走看看