Power Network POJ - 1459(EK算法模板+详解)

题意:

总共有a个节点,其中有发电站b个、用户c个和调度器a-b-c个三种节点,每个发电站有一个最大发电量,每个用户有个最大接受电量,现在有d条有向边,边有一个最大的流量代表,最多可以流出这么多电,现在从发电站发电到用户,
问最多可以发多少电。

题目:

A power network consists of nodes (power stations, consumers and dispatchers) connected by power transport lines. A node u may be supplied with an amount s(u) >= 0 of power, may produce an amount 0 <= p(u) <= p max(u) of power, may consume an amount 0 <= c(u) <= min(s(u),c max(u)) of power, and may deliver an amount d(u)=s(u)+p(u)-c(u) of power. The following restrictions apply: c(u)=0 for any power station, p(u)=0 for any consumer, and p(u)=c(u)=0 for any dispatcher. There is at most one power transport line (u,v) from a node u to a node v in the net; it transports an amount 0 <= l(u,v) <= l max(u,v) of power delivered by u to v. Let Con=Σ uc(u) be the power consumed in the net. The problem is to compute the maximum value of Con.
在这里插入图片描述

An example is in figure 1. The label x/y of power station u shows that p(u)=x and p max(u)=y. The label x/y of consumer u shows that c(u)=x and c max(u)=y. The label x/y of power transport line (u,v) shows that l(u,v)=x and l max(u,v)=y. The power consumed is Con=6. Notice that there are other possible states of the network but the value of Con cannot exceed 6.
Input
There are several data sets in the input. Each data set encodes a power network. It starts with four integers: 0 <= n <= 100 (nodes), 0 <= np <= n (power stations), 0 <= nc <= n (consumers), and 0 <= m <= n^2 (power transport lines). Follow m data triplets (u,v)z, where u and v are node identifiers (starting from 0) and 0 <= z <= 1000 is the value of l max(u,v). Follow np doublets (u)z, where u is the identifier of a power station and 0 <= z <= 10000 is the value of p max(u). The data set ends with nc doublets (u)z, where u is the identifier of a consumer and 0 <= z <= 10000 is the value of c max(u). All input numbers are integers. Except the (u,v)z triplets and the (u)z doublets, which do not contain white spaces, white spaces can occur freely in input. Input data terminate with an end of file and are correct.
Output
For each data set from the input, the program prints on the standard output the maximum amount of power that can be consumed in the corresponding network. Each result has an integral value and is printed from the beginning of a separate line.
Sample Input
2 1 1 2 (0,1)20 (1,0)10 (0)15 (1)20
7 2 3 13 (0,0)1 (0,1)2 (0,2)5 (1,0)1 (1,2)8 (2,3)1 (2,4)7
(3,5)2 (3,6)5 (4,2)7 (4,3)5 (4,5)1 (6,0)5
(0)5 (1)2 (3)2 (4)1 (5)4
Sample Output
15
6
Hint
The sample input contains two data sets. The first data set encodes a network with 2 nodes, power station 0 with pmax(0)=15 and consumer 1 with cmax(1)=20, and 2 power transport lines with lmax(0,1)=20 and lmax(1,0)=10. The maximum value of Con is 15. The second data set encodes the network from figure 1.

分析:

因为是多源点多汇点,所以我们要设置一个总源点和总汇点,使得总源点0到其他各源点,其他各汇点到总汇点a+1,然后就是普通的最大流问题

#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int M=110;

int a/**节点*/,b/**发电站*/,c/**用户*/,d/**有向边输电路*/;
int dp[M][M]/**从u到v的容量*/,f[M][M]/**流量*/,e[M],pre[M];
/*通常可以把这些边想象成道路,流量就是这条道路的车流量,
容量就是道路可承受的最大的车流量。很显然的,流量<=容量。
而对于每个不是源点和汇点的点来说,可以类比的想象成没有存储功能的货物的中转站,
所有”进入”他们的流量和等于所有从他本身”出去”的流量。*/
int EK(int s,int t)//最基础的最大流求法,即bfs找增广路法
{
    int ans=0;
    queue<int>q;//把queue<int>q写在while(true)外面比写在里面快了500ms
    while(1)///不断地从起点开始寻找增广路,每次都对其进行增广
    {
        memset(e,0,sizeof(e));
        e[s]=inf;
        q.push(s);
/**找寻这么一条路,这条路从源点开始一直一段一段的连到了汇点,
并且,这条路上的每一段都满足流量f<容量dp,注意,是严格的<,而不是<=。*/
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            for(int v=0;v<=a+1;v++)
            {
                if(!e[v]&&dp[u][v]>f[u][v])
                {
                    e[v]=min(e[u],dp[u][v]-f[u][v]);
    /**找到这条路上的每一段的(容量-流量)的值当中的最小值delta。*/
                    pre[v]=u;
                    q.push(v);
                }
            }
        }
        if(!e[t])/***直到源点和汇点不连通,
也就是找不到增广路为止。当前的流量就是最大流*/         
           break;
        for(int u=t;u!=s;u=pre[u])
        {
            f[pre[u]][u]+=e[t];/**我们把这条路上每一段的流量
都加上这个delta,一定可以保证这个流依然是可行流,这是显然的。*/
            f[u][pre[u]]-=e[t];/**给程序一个”后悔”的机会,回溯搜索效率就上升到指数级了。
算法神奇的利用了一个叫做反向边的概念来解决这个问题。即每条边(I,j)都有一条反向边(j,i),反向边也同样有它的容量。*/
        }
        ans+=e[t];/**得到了一个更大的流,
他的流量是之前的流量+delta,而这条路就叫做增广路*/
    }
    return ans;
}
int main()
{
    while(~scanf("%d%d%d%d",&a,&b,&c,&d))
    {
        memset(dp,0,sizeof(dp));
        memset(f,0,sizeof(f));
        for(int i=0;i<d;i++)///d条输电路
        {
            int u,v,w;
            scanf(" (%d,%d)%d",&u,&v,&w);
           // scanf("(%d,%d)%d",&u,&v,&w);
            dp[u+1][v+1]+=w;
        }
        for(int i=0;i<b;i++)///增加一个超级源点,所给的发电站都和超级源点相连
        {
            int v,w;
            scanf(" (%d)%d",&v,&w);
           // scanf("%d%d",&v,&w);
            dp[0][v+1]+=w;
        }
        for(int i=0;i<c;i++)///增加一个超级汇点,所给的用户都和超级汇点相连
        {
            int u,w;
            scanf(" (%d)%d",&u,&w);
            //scanf("%d%d",&u,&w);
            dp[u+1][a+1]+=w;
        }
        printf("%d\n",EK(0,a+1));
    }
    return 0;
}

KM算法详解(转)
一、概念引入

首先要先清楚最大流的含义,就是说从源点到经过的所有路径的最终到达汇点的所有流量和。

流网络G=(V,E)是一个有向图,其中每条边(u,v)∈E均有一个非负容量c(u,v)>=0。如果(u,v)不属于E,则假定c(u,v)=0。流网络中有两个特别的顶点:源点s和汇点t。下图展示了一个流网络的实例(其中斜线左边的数字表示实际边上的流,右边的数字表示边的最大容量):
在这里插入图片描述
二、基础知识准备

对一个流网络G=(V,E),其容量函数为c,源点和汇点分别为s和t。G的流f满足下列三个性质:
容量限制:对所有的u,v∈V,要求f(u,v)<=c(u,v)。
反对称性:对所有的u,v∈V,要求f(u,v)=-f(v,u)。
流守恒性:对所有u∈V-{s,t},要求∑f(u,v)=0 (v∈V)。

容量限制说明了从一个顶点到另一个顶点的网络流不能超过设定的容量,就好像是一个管道只能传输一定容量的水,而不可能超过管道体积的限制;反对称性说明了从顶点u到顶点v的流是其反向流求负所得,就好像是当参考方向固定后,站在不同的方向看,速度一正一负;而流守恒性说明了从非源点或非汇点的顶点出发的点网络流之和为0,这有点类似于基尔霍夫电流定律,且不管什么是基尔霍夫电流定律,通俗的讲就是进入一个顶点的流量等于从该顶点出去的流量,如果这个等式不成立,必定会在该顶点出现聚集或是枯竭的情况,而这种情况是不应该出现流网络中的,所以一般的最大流问题就是在不违背上述原则的基础上求出从源点s到汇点t的最大的流量值,显然这个流量值应该定义为从源点出发的总流量或是最后聚集到t的总流量,即流f的值定义为|f|=∑f(s,v) (v∈V)。

在求解最大流的问题前,必须对三个概念有所了解:残留网络,增广路径和割。下面先给出这三个概念的基本内容。
a.在给定的流网络G=(V,E)中,设f为G中的一个流,并考察一对顶点u,v∈V,在不超过容量c(u,v)的条件下,从u到v之间可以压入的额外网络流量,就是(u,v)的残留容量,就好像某一个管道的水还没有超过管道的上限,那么就这条管道而言,就一定还可以注入更多的水。残留容量的定义为:cf(u,v)=c(u,v)-f(u,v)。而由所有属于G的边的残留容量所构成的带权有向图就是G的残留网络。下图就是上面的流网络所对应的残留网络:
在这里插入图片描述
残留网络中的边既可以是E中边,也可以是它们的反向边。只有当两条边(u,v)和(v,u)中,至少有一条边出现在初始网络中时,边(u,v)才会出现在残留网络中。下面是一个有关残留网络的定理,若f是G中的一个流,Gf是由G导出的残留网络,f’是Gf中的一个流,则f+f’是G中一个流,且其值|f+f’|=|f|+|f’|。证明时只要证明f+f’这个流在G中满足之前所讲述的三个原则即可。在这里只给出理解性的证明,可以想象如果在一个管道中流动的水的总流量为f,而在该管道剩余的流量中存在一个流f’可以满足不会超过管道剩余流量的最大限,那么将f和f’合并后,也必定不会超过管道的总流量,而合并后的总流量值也一定是|f|+|f’|。

b.增广路径p为残留网络Gf中从s到t的一条简单路径。根据残留网络的定义,在不违反容量限制的条件下,G中所对应的增广路径上的每条边(u,v)可以容纳从u到v的某额外正网络流。而能够在这条路径上的网络流的最大值一定是p中边的残留容量的最小值。这还是比较好理解的,因为如果p上的流大于某条边上的残留容量,必定会在这条边上出现流聚集的情况。所以我们将最大量为p的残留网络定义为:cf§=min{cf(u,v) | (u,v)在p上}。而结合之前在残留网络中定理,由于p一定是残留网络中从s到t的一条路径,且|f’|=cf§,所以若已知G中的流f,则有|f|+|cf§|>|f|且|f|+|cf§|不会超过容量限制。

扫描二维码关注公众号,回复: 11317344 查看本文章

c.流网络G(V,E)的割(S,T)将V划分成为S和T=V-S两部分,使得s∈S,t∈T。如果f是一个流,则穿过割(S,T)的净流被定义为f(S,T)=∑f(x,y) (x∈S,y∈T),割(S,T)的容量为c(S,T)。一个网络的最小割就是网络中所有割中具有最小容量的割。设f为G中的一个流,且(S,T)是G中的一个割,则通过割(S,T)的净流f(S,T)=|f|。因为f(S,T)=f(S,V)-f(S,S)=f(S,V)=f(s,V)+f(S-s,V)=f(s,V)=|f|(这里的公式根据f(X,Y)=∑f(x,y) (x∈X,y∈Y)的定义,以及前面的三个限制应该还是可以推出来的,这里就不细讲了)。有了上面这个定理,我们可以知道当把流不断增大时,流f的值|f|不断的接近最小割的容量直到相等,如果这时可以再增大流f,则f必定会超过某个最小的割得容量,则就会存在一个f(S,T)<=c(S,T)<|f|,显然根据上面的定理这是不可能。所以最大流必定不超过网络最小割的容量。

综合上面所讲,有一个很重要的定理:最大流最小割定理

如果f是具有源s和汇点t的流网络G=(V,E)中的一个流,则下列条件是等价的:
1) f是G中一个最大流。
2) 残留网络Gf不包含增广路径。
3) 对G的某个割(S,T),有|f|=c(S,T)。

猜你喜欢

转载自blog.csdn.net/zeng_jun_yv/article/details/105717716