算法设计与分析:第六章 图 6.3网络最大流问题

/*
网络最大流问题:
指的是连通的赋权有向图D=(V,E),其中V是该图的顶点集,E是有向边集。
网络上的流:是指定义在有向边集合E上的一个函数f={f(u,v)},并称
f(u,v)=(u,v)的流量
V中有一个源点s,一个汇点t,网络上的流都是由源点流出最终流入汇点。
E中的每一条有向边(u,v)都有一个对应的容量上限c(u,v),通过这条边的流不能
超过容量上限。
在既不是源点也不是汇点的任一顶点v中,总的进入六必须等于总的发出流。
最大流问题:是指从源点到汇点可通过的最大流量。

简单最大流算法:
G:原图
Gf:流图,表示在算法的任一阶段已经达到的流,初始时所有边没有流。
Gr:残余图,G-Gf
在每个阶段,寻找图Gr中从s到t的一条路径,称为增长路径,这条路径上的
最小边值就是可以添加到路径上边的流量。通过调整Gf和重新计算Gr求解,
当Gr中没有从s到t的一条路径为止。

算法思路:
1根据残量网络计算层次图【用BFS建立分层图,分层图是以当前图为基础建立的,所以重复建立分层图】
2在层次图中使用DFS进行增广指导不存在增广路【使正向边流量减少,反向边流量增加】
3重复以上步骤直到无法增广

层次图:从原点到某点的最短距离分层的图,距离相等的为一层,比如上图中{1},{2,4},{3}
反向弧:对于一体哦有向边,需要建立另外一条反向边,当正向边剩余流量减少时,反向弧剩余流量增加

DFS只在分层图中DFS,DFS的下一个结点的Dis(距离源点的距离)要比自己的Dis大1
获得这条路径的流量L:实现:DFS函数有参量low,代表从源点到现在最窄的(剩余流量最小)
的边的剩余流量,当DFS到汇点时,low就是我们要求的流量

关于反向弧:
为今后提供返回的机会,让前面不走这条路而走别的路





POJ1273
Drainage Ditches
Time Limit: 1000MS		Memory Limit: 10000K
Total Submissions: 57375		Accepted: 22078
Description

Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle. 
Input

The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.
Output

For each case, output a single integer, the maximum rate at which water may emptied from the pond.
Sample Input

5(边数) 4(顶点数)
1(起始顶点) 2(结束顶点) 40(流量值)
1 4 20
2 4 20
2 3 30
3 4 10
Sample Output

50

实际思路:
1接受输入数据
2调用BFS建立分层图
3寻找曾广路,累加流量;直到找不到增光路为止

步骤2细分为:
2.1初始化距离数组为-1,起始点距离为0,在队列中加入首结点,设定队尾=队首+1
2.2只要队列不空,就取出队首元素,然后对该结点,遍历其所有结点:找出没有访问过的结点,并且这两个结点连通
2.3另新结点的距离值为弹出结点的距离值加1,并且将该连通结点放入队列中
2.4如果汇点的距离值大于0,说明最终找到了一条增广路,返回成功标志

步骤3细分为:
3.1判断如果当前结点是汇点,返回增广流量;否则转3.2
3.2如果当前结点与某结点的连通并且该连通结点还是当前结点的下一层(找到了子节点),
那么就继续将该子节点进行查找,同时更新最窄流量为:原流量和当前边流量中的较小值
(能到汇点,最窄流量不为0)
3.3在正向边的基础上减去最窄流量,在反向边的基础上加上最窄流量,并返回最窄流量
*/


/*
关键:
1 			//易错,这里必须用+=,而不能用=,为什么?,难道存在多条路径吗
			g_iMatrix[iBeg][iEnd] += iFlow;
2 		while(machao_BFS(m))
		{
			//寻找一次增广中的最窄流量
			while(iRet = findFlow(1,iMinFlow,m))
			{
				iAnswer += iRet;
			}
3 //判断是否能增广成功
bool machao_BFS(int iNodeNum)
{
	//易错,这里需要不断出入队列,因此设大一些
	int iQueue[2000];
	int iHead = 0;
	int iRear = 1;
	//加入首结点
	iQueue[1] = 1;
	memset(g_iDis,-1,sizeof(g_iDis));
	g_iDis[1] = 0;
	//广度优先增广
	while(iHead < iRear)
	{
		int iCur = iQueue[++iHead];
		for(int i = 1 ; i <= iNodeNum ; i++)
		{
			//如果结点没有访问过,且与当前结点连通,那么就建立分层。易错,这里必须确保边的流量为正值
			if(g_iDis[i] < 0 && g_iMatrix[iCur][i] > 0)
			{
				g_iDis[i] = g_iDis[iCur] + 1;
				//将子节点加入到队列中
				iQueue[++iRear] = i;
			}
		}
	}
	//如果汇点的分层数大于0,说明可以连接到汇点
	if(g_iDis[iNodeNum] > 0)
	{
		return true;

4 //寻找一次增广中的最窄流量
int findFlow(int iNode,int iFlow,int iNodeNum)
{
	//未做出:缺少递归基,如果抵达汇点,就返回最窄流量
	if(iNode == iNodeNum)
	{
		return iFlow;
	}
	int iRet = 0;
	//对所有与之相连的结点判断:是否相连并且是下一层,如果是的话,就递归对该子节点进行增广
	for(int i = 1 ; i <= iNodeNum ; i++)
	{
		//易错,这里必须确保边上的流量为正值
		if(g_iMatrix[iNode][i] > 0 && (g_iDis[i] == g_iDis[iNode] + 1) && (iRet = findFlow(i,min(iFlow,g_iMatrix[iNode][i]),iNodeNum)))
		{
			g_iMatrix[iNode][i] -= iRet;
			g_iMatrix[i][iNode] += iRet;
			return iRet;
*/
#include 
#include 

using namespace std;
const int MAXSIZE = 250 + 1;
int g_iMatrix[MAXSIZE][MAXSIZE];
int g_iDis[MAXSIZE];


//判断是否能增广成功
bool machao_BFS(int iNodeNum)
{
	//易错,这里需要不断出入队列,因此设大一些
	int iQueue[2000];
	int iHead = 0;
	int iRear = 1;
	//加入首结点
	iQueue[1] = 1;
	memset(g_iDis,-1,sizeof(g_iDis));
	g_iDis[1] = 0;
	//广度优先增广
	while(iHead < iRear)
	{
		int iCur = iQueue[++iHead];
		for(int i = 1 ; i <= iNodeNum ; i++)
		{
			//如果结点没有访问过,且与当前结点连通,那么就建立分层。易错,这里必须确保边的流量为正值
			if(g_iDis[i] < 0 && g_iMatrix[iCur][i] > 0)
			{
				g_iDis[i] = g_iDis[iCur] + 1;
				//将子节点加入到队列中
				iQueue[++iRear] = i;
			}
		}
	}
	//如果汇点的分层数大于0,说明可以连接到汇点
	if(g_iDis[iNodeNum] > 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int min(int a,int b)
{
	return a < b ?  a : b;
}

//寻找一次增广中的最窄流量
int findFlow(int iNode,int iFlow,int iNodeNum)
{
	//未做出:缺少递归基,如果抵达汇点,就返回最窄流量
	if(iNode == iNodeNum)
	{
		return iFlow;
	}
	int iRet = 0;
	//对所有与之相连的结点判断:是否相连并且是下一层,如果是的话,就递归对该子节点进行增广
	for(int i = 1 ; i <= iNodeNum ; i++)
	{
		//易错,这里必须确保边上的流量为正值
		if(g_iMatrix[iNode][i] > 0 && (g_iDis[i] == g_iDis[iNode] + 1) && (iRet = findFlow(i,min(iFlow,g_iMatrix[iNode][i]),iNodeNum)))
		{
			g_iMatrix[iNode][i] -= iRet;
			g_iMatrix[i][iNode] += iRet;
			return iRet;
		}
	}
	return 0;
}

void process()
{
	int n,m;
	int iBeg,iEnd,iFlow;
	while(cin >> n >> m)
	{
		//构建邻接矩阵
		int iRet = 0;
		int iMinFlow = 0x7fffffff;
		memset(g_iMatrix,0,sizeof(g_iMatrix));
		for(int i = 1 ; i <= n ; i++)
		{
			cin >> iBeg >> iEnd >> iFlow ;
			//易错,这里必须用+=,而不能用=,为什么?,难道存在多条路径吗
			g_iMatrix[iBeg][iEnd] += iFlow;
		}
		//开始循环增广
		int iAnswer = 0 ;
		while(machao_BFS(m))
		{
			//寻找一次增广中的最窄流量
			while(iRet = findFlow(1,iMinFlow,m))
			{
				iAnswer += iRet;
			}
		}
		cout << iAnswer << endl;
	}
}

int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(算法设计与分析)