POJ 1273 Drainage Ditches

题目链接:Drainage Ditches

解题思路:最大流问题,看了一下午的文章和博客。感觉对最大流有了一个初步的了解。给你一张图,里面部分节点间有容量为C的管道,里面的水流不能大于容量限制,给一个指定的源点和汇点,求汇点可以流入水的最大量。

首先要把整个图转化一下才可以更轻松的解决问题,将所有的边转化为一对边,一个称之为正向边,另一个为反向边。例如u, v节点间有一个容量为C的边,就可以抽象成一个C的正向边u -> v,和一个0的反向边 v -> u。正向边表示可以继续增大这个方向的流向最大值为多少,反向变表示可以减少这个方向的流量,也就是退流,最大值为多少。

其次要将整个图分层,用bfs对于没一个节点给予一个深度值,如果汇点没有被访问到那么就是说汇点没有连通。再在这个分层的图中找到一条严格按照深度增加的路径通往汇点,看这条路径上面的流量可否增加。(这里用DFS,也可以自己模拟栈)重复分层,因为之前有些满流量的边被删去了,所以这里新建的图汇点的层数会有变化,这里会找到新的路径,如果路径找不到,说明所有连向汇点的边已经全部满流量了,这是就可以输出了。否则继续此过程。

PS:这里说的可能不是很严谨,但是是我自己的领悟,其他的东西可以看代码。

DINIC算法 复杂度为O(n^2 * m)

#include<stdio.h>
#include<string.h>
#include<queue>
#include<vector>
#define INF 0x5fffffff
#define MAX 210

using namespace std;

typedef struct A{
	int s, e, c;
	A(){}
	A(int a, int b, int d){
		s = a, e = b, c = d;
	}
}edge;

vector<edge> Map[MAX];
int dist[MAX];
bool vis[MAX];
queue<int> que;
int n, m, s, e, c;

void addedge(int u, int v, int c){//增加边,这里将一条边拆分为两条
	Map[u].push_back(edge(u, v, c));//正向边
	Map[v].push_back(edge(v, u, 0));//反向边
}

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

void bfs(){//这里给图中每一个节点标记深度值dist
	int i, j, k;
	memset(dist, 0, sizeof(dist));
	memset(vis, false, sizeof(vis));
	while(!que.empty()) que.pop();
	que.push(1);
	vis[1] = true;
	while(!que.empty()){
		int s = que.front();
		que.pop();
		for(i = 0; i < Map[s].size(); i++){
			if(!vis[Map[s][i].e] && Map[s][i].c){//这里必须有容量才能表示这条边相通,可以再流过水流
				que.push(Map[s][i].e);
				dist[Map[s][i].e] = dist[s] + 1;//深度 + 1
				vis[Map[s][i].e] = true;//状态标记
			}
		}
	}	
}

int dfs(int u, int ret){//u为当前的节点 ret为当前可以增加的流量
	int i, j, ans = 0;
	if(u == m){
		return ret;
	}
	for(i = 0; i < Map[u].size(); i++){
		//edge tem = Map[u][i];
		if(Map[u][i].c && dist[Map[u][i].e] == dist[u] + 1){
			int aa = minNum(ret, Map[u][i].c);//一条路线上要保持每条管道内的流量不超过流量限制
			int dd = dfs(Map[u][i].e, aa);
			Map[u][i].c -= dd;//如果可以增加这里要在正向边减去相应的值
			for(j = 0; j < Map[Map[u][i].e].size(); j++){//反向边要加上这个值
				if(Map[Map[u][i].e][j].e == u){
					Map[Map[u][i].e][j].c += dd;
					break;
				}
			}
			ret -= dd;//此点流过最大的流量值
			ans += dd;//累计此点变化的流量值
		}
	}	
	return ans;
}

int maxflow(){
	int ret = 0;
	while(true){//每深搜一次都要在标记一次深度值
		bfs();
		if(!vis[m]){
			return ret;
		}
		ret += dfs(1, INF);
	}
} 

int main(){
	int i, j, k;
	//freopen("in.txt", "r", stdin);
	while(scanf("%d%d", &n, &m) != EOF){
		for(i = 0; i < MAX; i++){
			Map[i].clear();
		}
		for(i = 0; i < n; i++){
			scanf("%d%d%d", &s, &e, &c);
			addedge(s, e, c);
		}
		int tem = maxflow();
		printf("%d\n", tem);
	}
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值