基础网络流,这次使用一下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
是在当次循环执行过以后(当次循环中没有执行return
或break
),在执行下次循环时才++,这样表示再从这条边走一定不可能再增广了,不用再尝试了。
第一种写法
#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