序:
在以前的博文中,我解釋了關於最大流的EK與Dinic算法,以及它們的STL/非STL的實現(其實沒什麼區別)。本次講解的是ISAP算法。‘I’,指 improved,也就是說ISAP實際上是SAP算法的改進。目前沒有官方名稱。ios
通過測試,ISAP的效率在洛谷的板子題中遠勝於EK和Dinic的,速度大概是它們的2-3倍。代碼量實際上並無多大變化,在20行讀入優化與不壓行的狀況下(即下文代碼),200-210行。(若是壓行的話,120行問題不大。不要問我爲何,我空行太多…若是加讀入優化的話,再加上15行)git
本文給出三種實現方式:遞歸,非遞歸,STL,非STL,將ISAP用結構體封裝,直接調用函數(每種包含幾個,實際上並無什麼差異)。算法
舒適提示:
在開啓O2優化後,vector+queue版的ISAP比數組模擬的要快一點點(10ms?,運行時間均在90-110ms之間)。
若是不開優化,數組模擬比STL版要快2-3倍,達到120-150ms,而STL版是350-400ms(屢次提交測試)。
什麼級別的考試用什麼,這應該很好取捨。數組
ISAP的思路:
當咱們使用Dinic跑最大流的時候,咱們屢次進行了BFS分層,直到匯點不在殘餘網絡之中。而ISAP則只BFS分層一次(事實上你都甚至能夠不進行分層,也就比分層慢一點)。ISAP經過新的方式分層。markdown
對於任意一個節點x,當跑完數次最大流以後,咱們發現全部與它相連的節點都不知足level[i]+1 == level[x]了。出現這種狀況,Dinic的作法是從新分層,而ISAP的作法是將level[x]置成min(level[i])+1,從而構造出一條可行流。網絡
固然,這是有邊界的。咱們從匯點開始分層,源點的層數最高是n-1(一條鏈),因此當level[st] >= n的時候,算法結束,殘餘網絡中必定不存在可行流了。
這種方式也就解釋了爲何能夠不預先分層。由於這樣,分層的工做就留給了每一次推動可行流之中(不然沒法找到可行流)。函數
注:分層時從匯點分層。測試
可是若是僅僅是這樣,實際上快不了多少。優化
真正的優化在於這兩個:gap優化和當前弧(cur)優化。ui
gap優化:
Dinic的反覆分層被改爲了ISAP的向上構造,而可行流必須知足level[起點] = level[終點]+1,也就是說若是出現了斷層,也就是說在某一level的層級中不存在節點,那麼就不會再出現可行流(好比這一層是x,那麼流沒法從lev.(x-1)流到lev.(x+1)),直接結束算法,比不加它的ISAP快到不知哪裏去了。
cur優化(當前弧):
對於任意一個節點x,假設最大流已經通過它流向下一個節點,然後在某次又流到了它(好比說已經找到一條可行流,從源點從新尋找可行流,再次到達該節點),那麼該可行流必定不能再流到以前流過的那些弧了(那些弧已經流過了,再進去至關於又走了一遍老路,可是上一次已經最大化的利用了它的流量限制,也就是說此次是沒有任何意義的行爲。)
爲了不這種狀況,咱們用一個數組cur[]來記錄每一個節點當前流到的弧的編號(這是邊表存儲的狀況。若是是鄰接矩陣存儲,那就記錄當前流到了下一個節點的編號),當下一次又通過該節點的時候,咱們只須要再去嘗試沒有流過的弧了。
舉個例子:
節點5有三條弧,分別是arc1,arc2,arc3 。在以前尋找可行流的過程當中,通過了arc1。那麼將cur[5] = arc2,下一次便從arc2開始尋找可行流。
可能會有這樣一個問題:
憑什麼就說arc1就沒有再走的價值了,那若是上一次經過arc1流向了節點6,而可行流最終是經過節點6的某一條弧流向匯點的,可是節點6不必定只有一條弧啊,下一次還能夠走節點6的其餘弧。
解釋也很簡單,這是遞歸的過程,若是流已經流回到了節點5,那麼說明節點6以及arc1所抵達的全部節點都已經進行了這項操做。
固然,若是出現對某結點從新分層的時候,要將該節點的cur置回0(由於當前已經到尾了)。
代碼實現:
1.STL+DFS+非結構體版:
/* About: Max_flow ISAP STL+DFS Auther: kongse_qi Date: 2017/04/30 */
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#include <queue>
#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f
#define read(x) Get_Int(), x = in
#define p_b(x) push_back(x)
using namespace std;
struct Edge{
int st, en, weight;
Edge(){}
Edge(int s, int e, int w):
st(s), en(e), weight(w){}
}edge[maxm];
vector<int> arc[maxn];
typedef vector<int>::iterator iterator_t;
int n, m, st, en, tot;
int max_flow;
int num[maxn];
int dis[maxn];
int cur[maxn];
int pre[maxn];
char *X, *Buffer, c;
int in;
void Get_All()
{
fseek(stdin, 0, SEEK_END);
int file_lenth = ftell(stdin);
rewind(stdin);
Buffer = (char*)malloc(file_lenth*sizeof(char));
fread(Buffer, 1, file_lenth, stdin);
X = Buffer;
c = *X;
return ;
}
void Get_Int()
{
in = 0;
while(!isdigit(c)) c = *++X;
while(isdigit(c))
{
in = in*10+c-'0';
c = *++X;
}
return ;
}
void Init()
{
//freopen("test.in", "r", stdin);
Get_All();
tot = -1;
read(n), read(m);
read(st), read(en);
int a, b, c;
for(int i = 0; i != m; ++i)
{
read(a), read(b), read(c);
edge[++tot] = Edge(a, b, c);
edge[++tot] = Edge(b, a, 0);
arc[a].p_b(tot-1);
arc[b].p_b(tot);
}
return ;
}
bool Bfs()
{
bool wh[maxn];
memset(wh, 0, sizeof wh);
queue<int> q;
q.push(en);
wh[en] = true;
while(!q.empty())
{
int cur = q.front(); q.pop();
for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)
{
Edge& ne = edge[*i];
if(wh[ne.en] == false)
{
dis[ne.en] = dis[cur]+1;
q.push(ne.en);
wh[ne.en] = true;
}
}
}
if(wh[st] == false) return false;
return true;
}
int Augment(int x)
{
int minn = INF;
while(x != st)
{
Edge& e = edge[pre[x]];
minn = min(minn, e.weight);
x = e.st;
}
x = en;
while(x != st)
{
int curr = pre[x];
edge[curr].weight -= minn;
edge[curr^1].weight += minn;
x = edge[curr].st;
}
return minn;
}
void Advance(int x)
{
bool wh = false;
for(int& i = cur[x]; i != arc[x].size(); ++i)
{
Edge& e = edge[arc[x][i]];
if(e.weight > 0 && dis[x] == dis[e.en]+1)
{
wh = true;
pre[e.en] = arc[x][i];
x = e.en;
break;
}
}
if(wh == false)
{
int m = n-1;
for(iterator_t i = arc[x].begin(); i != arc[x].end(); ++i)
{
Edge& e = edge[*i];
if(e.weight > 0)
{
m = min(m, dis[e.en]);
}
}
if(--num[dis[x]] == 0) return ;
++num[dis[x] = m+1];
cur[x] = 0;
if(x != st) x = edge[pre[x]].st;
}
if(x == en)
{
max_flow += Augment(en);
x = st;
}
if(dis[st] >= n) return ;
Advance(x);
return ;
}
int Isap()
{
if(Bfs())
{
for(int i = 1; i != n+1; ++i)
{
++num[dis[i]];
}
Advance(st);
}
else return -1;
return max_flow;
}
int main()
{
Init();
printf("%d\n", Isap());
return 0;
}
2.struct+非STL+BFS版 (不開O2它最快)
/* About: Max_flow ISAP struct 非STL+BFS Auther: kongse_qi Date: 2017/04/30 */
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f
#define read(x) Get_Int(), x = in
using namespace std;
void Get_Int();
void Get_All();
char *X, *Buffer, c;
int in;
struct Edge{
int st, en, weight;
Edge(){}
Edge(int s, int e, int w):
st(s), en(e), weight(w){}
};
struct Isap{
int st, en, tot, n, m;
int max_flow;
int num[maxn];
int dis[maxn];
int cur[maxn];
int pre[maxn];
int arc[maxn][maxn/50];
int amount[maxn];
Edge edge[maxm];
void Init(int st, int en, int n, int m)
{
tot = -1;
this -> st = st; this -> en = en;
this -> n = n; this -> m = m;
max_flow = 0;
memset(cur, 0, sizeof cur);
memset(num, 0, sizeof num);
memset(amount, -1, sizeof amount);
return ;
}
void Add_edge(int s, int e, int w)
{
edge[++tot] = Edge(s, e, w);
edge[++tot] = Edge(e, s, 0);
arc[s][++amount[s]] = tot-1;
arc[e][++amount[e]] = tot;
return ;
}
bool Bfs()
{
bool wh[maxn];
memset(wh, 0, sizeof wh);
int q[maxn], st_pos = -1, en_pos = -1;
q[++en_pos] = en;
wh[en] = true;
while(st_pos != en_pos)
{
int cur = q[++st_pos];
for(int i = 0; i != amount[cur]+1; ++i)
{
Edge& ne = edge[arc[cur][i]];
if(wh[ne.en] == false)
{
dis[ne.en] = dis[cur]+1;
q[++en_pos] = ne.en;
wh[ne.en] = true;
}
}
}
if(wh[st] == false) return false;
return true;
}
int Augment(int x)
{
int minn = INF;
while(x != st)
{
Edge& e = edge[pre[x]];
minn = min(minn, e.weight);
x = e.st;
}
x = en;
while(x != st)
{
int curr = pre[x];
edge[curr].weight -= minn;
edge[curr^1].weight += minn;
x = edge[curr].st;
}
return minn;
}
void Advance(int x)
{
while(dis[st] < n)
{
bool wh = false;
for(int& i = cur[x]; i != amount[x]+1; ++i)
{
Edge& e = edge[arc[x][i]];
if(e.weight > 0 && dis[x] == dis[e.en]+1)
{
wh = true;
pre[e.en] = arc[x][i];
x = e.en;
break;
}
}
if(wh == false)
{
int m = n-1;
for(int i = 0; i != amount[x]+1; ++i)
{
Edge& e = edge[arc[x][i]];
if(e.weight > 0)
{
m = min(m, dis[e.en]);
}
}
if(--num[dis[x]] == 0) return ;
++num[dis[x] = m+1];
cur[x] = 0;
if(x != st) x = edge[pre[x]].st;
}
if(x == en)
{
max_flow += Augment(en);
x = st;
}
}
return ;
}
int Max_flow()
{
Bfs();
for(int i = 1; i != n+1; ++i)
{
++num[dis[i]];
}
Advance(st);
return max_flow;
}
}doit;
int main()
{
int a, b, c, n, m, st, en;
freopen("test.in", "r", stdin);
Get_All();
read(n), read(m);
read(st), read(en);
doit.Init(st, en, n, m);
for(int i = 0; i != m; ++i)
{
read(a), read(b), read(c);
doit.Add_edge(a, b, c);
}
printf("%d\n", doit.Max_flow());
return 0;
}
void Get_All()
{
fseek(stdin, 0, SEEK_END);
int file_lenth = ftell(stdin);
rewind(stdin);
Buffer = (char*)malloc(file_lenth*sizeof(char));
fread(Buffer, 1, file_lenth, stdin);
X = Buffer;
c = *X;
return ;
}
void Get_Int()
{
in = 0;
while(!isdigit(c)) c = *++X;
while(isdigit(c))
{
in = in*10+c-'0';
c = *++X;
}
return ;
}
3 struct+STL+BFS版
/* About: Max_flow ISAP struct STL+BFS Auther: kongse_qi Date: 2017/04/30 */
#pragma GCC optimize(3)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#include <queue>
#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f
#define read(x) Get_Int(), x = in
#define p_b(x) push_back(x)
using namespace std;
void Get_Int();
void Get_All();
char *X, *Buffer, c;
int in;
typedef vector<int>::iterator iterator_t;
struct Edge{
int st, en, weight;
Edge(){}
Edge(int s, int e, int w):
st(s), en(e), weight(w){}
};
struct Isap{
int st, en, tot, n, m;
int max_flow;
int num[maxn];
int dis[maxn];
int cur[maxn];
int pre[maxn];
vector<int> arc[maxn];
Edge edge[maxm];
void Init(int st, int en, int n, int m)
{
tot = -1;
this -> st = st; this -> en = en;
this -> n = n; this -> m = m;
for(int i = 1; i != n+1; ++i)
{
arc[i].clear();
}
max_flow = 0;
memset(cur, 0, sizeof cur);
memset(num, 0, sizeof num);
return ;
}
void Add_edge(int s, int e, int w)
{
edge[++tot] = Edge(s, e, w);
edge[++tot] = Edge(e, s, 0);
arc[s].p_b(tot-1);
arc[e].p_b(tot);
return ;
}
bool Bfs()
{
bool wh[maxn];
memset(wh, 0, sizeof wh);
queue<int> q;
q.push(en);
wh[en] = true;
while(!q.empty())
{
int cur = q.front(); q.pop();
for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)
{
Edge& ne = edge[*i];
if(wh[ne.en] == false)
{
dis[ne.en] = dis[cur]+1;
q.push(ne.en);
wh[ne.en] = true;
}
}
}
if(wh[st] == false) return false;
return true;
}
int Augment(int x)
{
int minn = INF;
while(x != st)
{
Edge& e = edge[pre[x]];
minn = min(minn, e.weight);
x = e.st;
}
x = en;
while(x != st)
{
int curr = pre[x];
edge[curr].weight -= minn;
edge[curr^1].weight += minn;
x = edge[curr].st;
}
return minn;
}
void Advance(int x)
{
while(dis[st] < n)
{
bool wh = false;
for(int& i = cur[x]; i != arc[x].size(); ++i)
{
Edge& e = edge[arc[x][i]];
if(e.weight > 0 && dis[x] == dis[e.en]+1)
{
wh = true;
pre[e.en] = arc[x][i];
x = e.en;
break;
}
}
if(wh == false)
{
int m = n-1;
for(iterator_t i = arc[x].begin(); i != arc[x].end(); ++i)
{
Edge& e = edge[*i];
if(e.weight > 0)
{
m = min(m, dis[e.en]);
}
}
if(--num[dis[x]] == 0) return ;
++num[dis[x] = m+1];
cur[x] = 0;
if(x != st) x = edge[pre[x]].st;
}
if(x == en)
{
max_flow += Augment(en);
x = st;
}
}
return ;
}
int Max_flow()
{
Bfs();
for(int i = 1; i != n+1; ++i)
{
++num[dis[i]];
}
Advance(st);
return max_flow;
}
}doit;
int main()
{
int a, b, c, n, m, st, en;
//freopen("test.in", "r", stdin);
Get_All();
read(n), read(m);
read(st), read(en);
doit.Init(st, en, n, m);
for(int i = 0; i != m; ++i)
{
read(a), read(b), read(c);
doit.Add_edge(a, b, c);
}
printf("%d\n", doit.Max_flow());
return 0;
}
void Get_All()
{
fseek(stdin, 0, SEEK_END);
int file_lenth = ftell(stdin);
rewind(stdin);
Buffer = (char*)malloc(file_lenth*sizeof(char));
fread(Buffer, 1, file_lenth, stdin);
X = Buffer;
c = *X;
return ;
}
void Get_Int()
{
in = 0;
while(!isdigit(c)) c = *++X;
while(isdigit(c))
{
in = in*10+c-'0';
c = *++X;
}
return ;
}
結構體版實際上就是將函數做爲結構體的成員函數運行,在主函數中調用時邏輯會更加清晰(分區很明顯)。
實測結果:
O2優化狀況下:
1.耗時:115ms
內存:17281kb
2.耗時:80ms
內存:23304kb
3.耗時:103ms
內存:17281kb
其實差距不大。
不開O2的狀況下:
1.耗時:322ms
內存:17429kb
2.耗時:141ms
內存:23304kb
3.耗時:323ms
內存:17292kb
(結果不必定十分穩定)
所以不開優化的STL是很是不建議使用的。
STL的快速與便捷只有在開啓優化的狀況下才能兼得。
附壓行代碼:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#include <vector>
#define maxn 10005
#define maxm 200005
#define INF 0x3f3f3f
using namespace std;
struct Edge{
int st, en, weight;
Edge(){}
Edge(int s, int e, int w):
st(s), en(e), weight(w){}
};
struct Isap{
int st, en, tot, n, m, max_flow;
int num[maxn], dis[maxn], cur[maxn], pre[maxn], arc[maxn][100], amount[maxn];
vector<Edge> edge;
void Init(int st, int en, int n, int m){
tot = -1;
this -> st = st; this -> en = en;
this -> n = n; this -> m = m;
max_flow = 0;
edge.resize(m*2);
memset(cur, 0, sizeof cur);
memset(num, 0, sizeof num);
memset(amount, -1, sizeof amount);
}
void Add_edge(int s, int e, int w){
edge[++tot] = Edge(s, e, w);
edge[++tot] = Edge(e, s, 0);
arc[s][++amount[s]] = tot-1;
arc[e][++amount[e]] = tot;
}
void Bfs(){
bool wh[maxn] = {0};
int q[maxn], st_pos = -1, en_pos = -1;
q[++en_pos] = en;
wh[en] = true;
while(st_pos != en_pos){
int cur = q[++st_pos];
for(int i = 0; i != amount[cur]+1; ++i){
Edge ne = edge[arc[cur][i]];
if(wh[ne.en] == false){
dis[ne.en] = dis[cur]+1;
q[++en_pos] = ne.en;
wh[ne.en] = true;
}
}
}
}
int Augment(){
int minn = INF, curr;
Edge e;
for(int x = en; x != st; ){
e = edge[pre[x]];
minn = min(minn, e.weight);
x = e.st;
}
for(int x = en; x != st;){
curr = pre[x];
edge[curr].weight -= minn;
edge[curr^1].weight += minn;
x = edge[curr].st;
}
return minn;
}
void Advance(int x){
while(dis[st] < n){
bool wh = false;
for(int& i = cur[x]; i != amount[x]+1; ++i){
Edge &e = edge[arc[x][i]];
if(e.weight > 0 && dis[x] == dis[e.en]+1){
wh = true;
pre[e.en] = arc[x][i];
x = e.en;
break;
}
}
if(wh == false){
int minn = n-1;
for(int i = 0; i != amount[x]+1; ++i){
Edge& e = edge[arc[x][i]];
if(e.weight > 0)
minn = min(minn, dis[e.en]);
}
if(--num[dis[x]] == 0) return ;
++num[dis[x] = minn+1];
cur[x] = 0;
if(x != st) x = edge[pre[x]].st;
}
if(x == en) {
max_flow += Augment();
x = st;
}
}
}
int Max_flow(){
Bfs();
for(int i = 1; i != n+1; ++i)
++num[dis[i]];
Advance(st);
return max_flow;
}
}doit;
int main(){
int a, b, c, n, m, st, en;
//freopen("test.in", "r", stdin);
scanf("%d%d", &n, &m);
st = 1, en = n;
doit.Init(st, en, n, m);
for(int i = 0; i != m; ++i){
scanf("%d%d%d", &a, &b, &c);
doit.Add_edge(a, b, c);
}
printf("%d\n", doit.Max_flow());
return 0;
}
(121行total)
自此結束。
箜瑟_qi 2017.04.30 23:20 四月最後一篇。