嗯接着前篇…有了前面的原理铺垫,接下来的可能会简单一些…?
FF看见烦,SAP不想学。
所以只剩下EK和Dinic了。
模板题:luogu3376
基本上上把EK的queue改成stack就成了FF,所以这里不讨论FF的问题。
先贴代码,然后再说一说其含义。
#include
using namespace std;
#define MAXN 100003
struct node{
int fr, to, va; //从fr到to有一条权值为v的边
int nxt;
}edge[MAXN];
int cnt, head[MAXN];
inline void read(int &x) {
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
inline void add_edge(int u, int v, int w) { //邻接表存边
edge[cnt].fr = u, edge[cnt].to = v, edge[cnt].va = w;
edge[cnt].nxt = head[u], head[u] = cnt++;
edge[cnt].fr = v, edge[cnt].to = u, edge[cnt].va = 0;
edge[cnt].nxt = head[v], head[v] = cnt++;
}
int pre[MAXN];
bool vis[MAXN];
int st, ed, n, m;
bool BFS (int S, int T) {
queue<int> q;
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
q.push(S); vis[S] = 1;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = head[u]; i != -1; i = edge[i].nxt) {
int tmp = edge[i].to;
if(edge[i].va <= 0 || vis[tmp]) continue;
pre[tmp] = i; //记录路径
vis[tmp] = 1;
if(tmp == T) return 1;
q.push(tmp);
}
}
return false;
}
int ans;
int EK() {
int sub;
while(BFS(st, ed)) {
sub = 0x3fffff;
int i = pre[ed];
while(i != -1) {
sub = min(sub, edge[i].va);
i = pre[edge[i].fr];
}
i = pre[ed];
while(i != -1) {
edge[i].va -= sub;
edge[i ^ 1].va += sub;
i = pre[edge[i].fr];
/*
我们要求剩余权值
而如果每次将边权减去更新部分之后
求个最小值 即可得到路径上的最小剩余
反边就对应的加上这个最小剩余
由于反边和当前边的编号一定相差1,
所以可以直接^得到
*/
}
ans += sub;
}
return ans;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
read(n), read(m), read(st), read(ed);
int x, y, z;
memset(head, -1, sizeof head);
for(int i = 1; i <= m; ++i) {
read(x), read(y), read(z);
add_edge(x, y, z);
}
cout<return 0;
}
其实EK的思路十分简单,就是无数次的BFS直到能够找到我们目标的最大流。
该方法luogu70,除了三个TLE之外都是0ms。该方法非常容易写,足够应付一些数据比较小的题。
考虑一下复杂度,据说是 O(VE2) ,显然不能接受。怎么来的?希腊奶。
最负盛名的网络流算法,也是最为实用的。
我们先略过最小弧优化,考虑一下朴素的Dinic。
#include
using namespace std;
#define MAXE 1000003
struct node{
int fr, to, va;
int nxt;
}edge[MAXE];
int head[MAXV], n, m, cnt, st, ed;
inline void read(int &x) {
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
inline void add_edge(int u, int v, int w) {
edge[cnt].fr = u, edge[cnt].to = v, edge[cnt].va = w;
edge[cnt].nxt = head[u], head[u] = cnt++;
edge[cnt].fr = v, edge[cnt].to = u, edge[cnt].va = 0;
edge[cnt].nxt = head[v], head[v] = cnt++;
}
int deep[MAXV];
bool BFS() {
memset(deep, 0, sizeof deep);
queue<int> q;
q.push(st);
deep[st] = 1;
while(!q.empty()) {
int tmp = q.front();
q.pop();
for(int i = head[tmp]; i != -1; i = edge[i].nxt) {
int v = edge[i].to;
if( deep[v] || edge[i].va <= 0) continue;
deep[v] = deep[tmp] + 1;
q.push(v);
}
}
return deep[ed];
}
int dfs(int u, int flow) {
//flow为到达终点最多能增广的值
if(u == ed) return flow;
int add = 0;
//u点的最大增广量
for(int i = head[u]; i != -1 && add < flow; i = edge[i].nxt) {
int v = edge[i].to;
if(deep[v] != deep[u] + 1) continue;
if(!edge[i].va) continue;
int tmpadd = dfs(v, min(edge[i].va, flow - add));
edge[i].va -= tmpadd;
edge[i ^ 1].va += tmpadd;
add += tmpadd;
}
return add;
}
int Dinic() {
int ans = 0;
while(BFS())
ans += dfs(st, 0x3fffff);
return ans;
}
int main() {
memset(head, -1, sizeof head);
scanf("%d%d%d%d", &n, &m, &st, &ed);
int x, y, z;
for(int i = 0; i < m; ++i) {
read(x), read(y), read(z);
add_edge(x, y, z);
}
cout<return 0;
}
蒟蒻不会手动递归.jpg
考虑一下我们刚才的EK算法,我们首先BFS出了一个可行的最短路,然后以这个最短路为基准转移的。
…求豆麻袋。
为什么我们要增广一次就BFS一次啊,刚才我们在一次BFS中,求出了一条最短路,但是,考虑一棵最短路树,这个最短路只是这棵树一个小小的枝儿。既然如此,何不继续在这个树上搞呢?
首先,我们先BFS出从st到ed的路径距离,然后构造一棵最短路树,然后,我们对这棵最短路树进行DFS操作,最后把得到的结果加起来。完成。
https://www.cnblogs.com/smartweed/p/5865727.html
http://www.cnblogs.com/zsboy/archive/2013/01/27/2878810.html