POJ 1273 Drainage Ditches

題目鏈接:http://poj.org/problem?id=1273

解析:求最大流的一題入門題,使用的是效率不太高的EK算法,不會ek的話可以研究一下這個博客

http://www.cnblogs.com/devil-91/archive/2012/08/17/2644569.html註釋的很詳細

關於反向邊爲什麼加最大流,可以看下面,(轉載
----------------------------------------------------我是分割線~\(≧▽≦)/~--------------------------------------------------

在這幅圖中我們首先要增廣1->2->4->6,這時可以獲得一個容量爲 2的流,但是如果不建立4->2反向弧的話,則無法進一步增廣,最終答案爲2,顯然是不對的,然而如果建立了反向弧4->2,則第二次能進行 1->3->4->2->5->6的增廣,最大流爲3.

 

Comzyh對反向弧的理解可以說是"偷樑換柱",請仔細閱讀: 在上面的例子中,我們可以看出,最終結果是1->2->5->6和1->2->4->6和 1->3->4->6.當增廣完1->2->4->6(代號A)後,在增廣 1->3->4->2->5->6(代號B),相當於將經過節點2的A流從中截流1(總共是2)走2->5>6,而不走2->4>6了,同時B流也從節點4截流出1(總共是1)走4->6而不是4->2->5->6,相當於AB流做加法.

 

簡單的說反向弧爲今後提供反悔的機會,讓前面不走這條路而走別的路.

 

//EK算法
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
#define Max 1000000000
using namespace std;

int n,m,ans;
int cap[210][210],pre[210],flow[210];	//pre[]存的是該點的前驅,flow[]是起始點到該點的最大流

int bfs(int start,int end)	//每一次bfs,就找到一條由start到end的路
{
	queue<int>Q;
	while(!Q.empty())
		Q.pop();
	memset(pre,-1,sizeof(pre));
	memset(flow,0,sizeof(flow));//初始化
	flow[start] = Max;
	pre[start] = 0;
	Q.push(start);
	while(!Q.empty())
	{
		int temp = Q.front();
		Q.pop();
		if(temp == end)
			break;
		for(int i = 1; i <= m;i++)
		{
			if(cap[temp][i] > 0 &&pre[i] == -1)	//pre[i]==-1說明他還沒進入過隊列,保證bfs不會往回走
			{
				pre[i] = temp;
				flow[i] = min(flow[temp],cap[temp][i]);
				Q.push(i);
			}
		}
	}
	if(flow[end] == 0)	//代表途中已經沒有start到end的路了
		return 0;
	else
		return flow[end];
}

void ek(int start,int end)
{
	while(1)
	{
		int t = bfs(start,end); //t存儲的是該路的最大流
		if(t == 0)
			break;
		for(int k = end; k != start; k = pre[k])	
		{
			cap[pre[k]][k] -= t;		//正向邊減去最大流
			cap[k][pre[k]] += t;		//反向邊加上最大流
		}
		ans += t;						//每一條邊都要加上纔是最大流
	}
}

int main()
{
	while(scanf("%d%d",&n,&m) != EOF)
	{
		memset(cap,0,sizeof(cap));
		memset(pre,0,sizeof(pre));
		memset(flow,0,sizeof(flow));
		int a,b,c;
		ans = 0;
		for(int i = 1; i <= n;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			cap[a][b] += c;	//可能有多個a->b的邊,則a->b的流量就是全部加在一起
	//		cap[b][a] -= c;	//所有邊的反向邊都是0,若爲-c的話,正向回來就是c,正向的流量就變成2*c了
		}

		ek(1,m);
		printf("%d\n",ans);
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章