HDU 3549 Flow Problem

14 篇文章 0 订阅
7 篇文章 0 订阅

传送门

基础网络流,这次使用一下dinic算法,BFS+DFS

  • BFS负责找一个流量可行(仅关注当前边)的层次图,DFS在层次图基础上找增广路,而边上流量的更新在DFS的回溯过程中负责。上述过程重复直到汇点(sink)在BFS层次遍历后不可达。
  • DFS有两种写法,可以一次只找一条路(在外面多次调用DFS直到返回值为0);也可以一次找多条路,在DFS内部累加flow

当前弧优化:每次BFS后都要初始化cur[],使得DFS在访问重复点时不重复访问该点的边。首先,这在第一种写法中是显而易见的作用。在第二种写法中,为什么DFS会重复访问点呢(层数为0和1的点在第二种写法中当然不会被重复访问)?不是在BFS中赋层次值时避免了吗?我的答案是:在BFS中没有重复访问点,这没错,但是,某点可能有两个或以上的前驱点是同一层次(都是当前点层次值-1)!而在DFS中仅关心层次是否相邻。
(但事实证明第一种写法优不优化耗时差别明显,第二种写法几乎没差别(但这可能和数据有关))

4月10日更新:关于当前弧优化再说几句。
注意当前弧优化中的i是在当次循环执行过以后(当次循环中没有执行returnbreak),在执行下次循环时才++,这样表示再从这条边走一定不可能再增广了,不用再尝试了。

第一种写法

#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

typedef long long LL;
#define oo 1e9
const int Nmax = 15 + 1;

struct Edge
{
	int f, t, cap, flow;
};
vector<Edge> ve;
vector<int> v[Nmax];
int level[Nmax];                     // BFS,记录层次
int cur[Nmax];                       // DFS,当前弧优化,初始化为0,每次BFS后初始化

void init(int t)
{
	ve.clear();
	for (int i = 1; i <= t; i++)
		v[i].clear();
}

void add_edge(int f, int t, int c)
{
	ve.push_back({ f,t,c,0 });
	ve.push_back({ t,f,0,0 });
	v[f].push_back(ve.size() - 2);
	v[t].push_back(ve.size() - 1);
}

bool bfs(int t)
{
	memset(level, -1, sizeof level);
	queue<int> q;
	q.push(1);
	level[1] = 0;
	for (; !q.empty();)
	{
		int x = q.front();
		q.pop();
		for (int i = 0; i < v[x].size(); i++)
		{
			Edge& e = ve[v[x][i]];
			if (level[e.t] < 0 && e.flow < e.cap)        // 不重复访问
			{
				level[e.t] = level[x] + 1;
				q.push(e.t);
			}
		}
	}
	return level[t] >= 0;
}

int dfs(int n, int t, int f)
{
	if (n == t || f == 0) return f;               // f等于0是因为调用时e.cap==e.flow了
	int f0;
	for (int& i = cur[n]; i < v[n].size(); i++)   // 当前弧优化,i是引用,可间接改变cur[]的值
	{
		Edge& e = ve[v[n][i]];
		if (level[e.t] == level[n] + 1 && (f0 = dfs(e.t, t, min(f, e.cap - e.flow))) > 0)
		{
			e.flow += f0;
			ve[v[n][i] ^ 1].flow -= f0;
			return f0;                                 //
		}
	}
	return 0;                                          //
}

int main()
{
	int now = 1;
	int N, M, T;
	scanf("%d", &T);
	for (; T--;)
	{
		int flow = 0;
		scanf("%d%d", &N, &M);
		init(N);
		int f, t, c;
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &f, &t, &c);
			add_edge(f, t, c);
		}
		for (; bfs(N);)
		{
			memset(cur, 0, sizeof cur);
			//flow += dfs(1, N, oo);
			int temp;
			for (; temp = dfs(1, N, oo);)           //
				flow += temp;
		}
		printf("Case %d: %d\n", now++, flow);
	}
	return 0;
}

// dinic算法
// cin/cout     608ms
// scanf/printf 140ms

第二种写法


#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;

typedef long long LL;
#define oo 1e9
const int Nmax = 15 + 1;

struct Edge
{
	int f, t, cap, flow;
};
vector<Edge> ve;
vector<int> v[Nmax];
int level[Nmax];                     // BFS,记录层次
int cur[Nmax];                       // DFS,当前弧优化,初始化为0,每次BFS后初始化

void init(int t)
{
	ve.clear();
	for (int i = 1; i <= t; i++)
		v[i].clear();
}

void add_edge(int f, int t, int c)
{
	ve.push_back({ f,t,c,0 });
	ve.push_back({ t,f,0,0 });
	v[f].push_back(ve.size() - 2);
	v[t].push_back(ve.size() - 1);
}

bool bfs(int t)
{
	memset(level, -1, sizeof level);
	queue<int> q;
	q.push(1);
	level[1] = 0;
	for (; !q.empty();)
	{
		int x = q.front();
		q.pop();
		for (int i = 0; i < v[x].size(); i++)
		{
			Edge& e = ve[v[x][i]];
			if (level[e.t] < 0 && e.flow < e.cap)        // 不重复访问
			{
				level[e.t] = level[x] + 1;
				q.push(e.t);
			}
		}
	}
	return level[t] >= 0;
}

int dfs(int n, int t, int f)
{
	if (n == t || f == 0) return f;               // f等于0是因为调用时e.cap==e.flow了
	int flow = 0, f0;
	for (int& i = cur[n]; i < v[n].size(); i++)   // 当前弧优化,i是引用,可间接改变cur[]的值
	{
		Edge& e = ve[v[n][i]];
		if (level[e.t] == level[n] + 1 && (f0 = dfs(e.t, t, min(f, e.cap - e.flow))) > 0)
		{
			e.flow += f0;
			ve[v[n][i] ^ 1].flow -= f0;
			flow += f0;
			f -= f0;                              // 这里f不会变为负值
			if (f == 0) break;
		}
	}
	return flow;
}

int main()
{
	int now = 1;
	int N, M, T;
	scanf("%d", &T);
	for (; T--;)
	{
		int flow = 0;
		scanf("%d%d", &N, &M);
		init(N);
		int f, t, c;
		for (int i = 0; i < M; i++)
		{
			scanf("%d%d%d", &f, &t, &c);
			add_edge(f, t, c);
		}
		for (; bfs(N);)
		{
			memset(cur, 0, sizeof cur);
			flow += dfs(1, N, oo);           // 这里有两种写法,另一种是这里多次调用dfs,而dfs内部不再累加flow,找到一条路后就立即返回
		}
		printf("Case %d: %d\n", now++, flow);
	}
    return 0;
}

// dinic算法
// cin/cout     608ms
// scanf/printf 140ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值