上下界网络流学习总结

上下界网络流学习总结:

导入

一般的,定义一个网络是一个加权的有向图G = (V, E, C), E中的每条弧 (u, v)都有一个容量上界 C(u, v) >= 0

如果人为的规定V中的两个点 s, t,其中 s没有入度而 t 没有出度; 并为 E 中的每条弧 (u, v)赋予一个值 f(u, v) >= 0, f 满足以下两个条件:

1、除 s, t 之外的任意一个点i 都满足

sigma f(u, i) = sigma f(i, v);

2、任意一条 E中的弧 (u, v), 都满足 f(u, v) <= c(u, v);

则称f是 G 的一个可行流, 称 s为流的源且 t 是流的会。 前一个条件被称为流量平衡条件, 后者则是容量限制条件

而如果一个可行流f使原点提供的流量和达到最大, 则称 f是 G最大流。

 

如果为 G 中的每条边再加入一个容量下界:令 G = (V, E, B, C), B(u, v)表示弧 (u, v)的容量下界。这样 G就是一个容量有上下界的流网络。类似的定义 G 中的可行流 f

1、 除 s, t 之外的任意一个点 i 都满足

sigma f(u, i) = sigma f(i, v);

2、 任意一条 E 中的弧 (u, v), 都满足 b(u, v) <= f(u, v) <= c(u, v);

同时我们可以定义 G 中的最大流fmax然而对于有容量下界的网络而言, 我们还可以定义最小流fmin使源点提供流量最小的流。

 

算法实现

1、无源汇可行流(没有确定源点和汇点,询问是否有可行的流量f)

(1)解答: 我们已经知道,普通的最大流只有上界的约束,但是对于上下界网络流,流量却必须大于等于一个下界。因此我们可以想到,设

f(u, v) = b(u, v) + g(u, v)

其中g(u, v) >= 0,这样就可以保证 f(u, v) >= b(u, v);同时为了满足上街限制,有

g(u, v) <= c(u, v) – b(u, v)

c’(u, v) = c(u, v) – b(u, v),则可以将 g(u, v)看作流网络 c’中的一个可行流。当然,直接转化显然是不对的,所以我们来尝试用流量守恒寻找思路, 对于任意一点i

sigma(b(u, i) + g(u, i)) = sigma(b(i, v) + g(i, v))

sigma(g(i, v)) – sigma(g(u, i)) = sigma(b(u, i)) – sigma(b(i, v))

设 :M(i) = sigma(b(u, i)) – sigma(b(i, v))

M(i) 为流入结点 i的下界和 减去流出 i的下界和。

如果M(i)非负, 我们设一个附加源点ss, 令 c’(ss, i) = M(i)

如果M(i)为负,则设一个附加汇点 tt, 令 c’(i, tt) = -M(i)

现在附加图构建完毕。

在这样一个加入附加源和附加汇的流网络 C中,如果任意 g(ss, i)g(i, tt)都达到满载,那么 C中的这一个可行流 g 一定对应原网络 G 中的一个可行流 f;反之 G 中的任意一个可行流 f 都可以对应 C中的一个 g(ss, i)g(i, tt)都满载的流。而让从附加源点流出的弧都满载的可行流,一定是一个从附加源到附加汇的最大流。因此,求原网络G 中的一个可行流等价于求 C中ss 至 tt 的最大流,并判断从源点流出的弧是否满载:如果满载,则问题有解,否则一定无解。

时间复杂度:和你用的网络流算法复杂度相同

(2)适用题目:

zoj 2314

分析:裸的可行流,但是最后求取流量的时候要注意加上之前的下界流量。

Source:

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p = false; c = getchar(); } while(isdigit(c)) v = (v << 3) + (v << 1) + (c ^ '0'), c = getchar(); if(!p) v = -v; } const int INF = ~0u >> 1; const int MAXN = 200 + 20; const int MAXM = MAXN * MAXN; int tot, s, t, n, m, x, y, z, T; int first[MAXN], dis[MAXN], temp[MAXN], nece[MAXN], low[MAXM]; struct node { int next, to, w; } edge[MAXM << 1]; inline void create(int x, int y, int w) { edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; edge[tot].w = w; tot++; } bool bfs(int s, int t) { memset(dis, -1, (t + 5 << 2)); memcpy(temp, first, (t + 5 << 2)); queue 
             
               q; q.push(s), dis[s] = 0; while(!q.empty()) { register int j = q.front(); q.pop(); for(register int p = first[j]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] == -1) { dis[edge[p].to] = dis[j] + 1; q.push(edge[p].to); if(edge[p].to == t) return true; } } } return false; } int dfs(int cur, int low, int t) { if(cur == t) return low; register int delta, res = 0; for(register int &p = temp[cur]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] > dis[cur]) { delta = dfs(edge[p].to, min(edge[p].w, low - res), t); if(delta) { edge[p].w -= delta, edge[p ^ 1].w += delta; res += delta; if(res == low) break; } } } if(res != low) dis[cur] = -1; return res; } void dinic() { register int ans = 0; while(bfs(s, t)) dfs(s, INF, t); } void read() { R(n), R(m), s = 0, t = n + 1; for(int i = 1; i <= m; ++i) { R(x), R(y), R(low[i]), R(z); nece[x] -= low[i], nece[y] += low[i]; create(x, y, z - low[i]); create(y, x, 0); } for(int i = 1; i <= n; ++i) if(nece[i] < 0) create(i, t, -nece[i]), create(t, i, 0); else create(s, i, nece[i]), create(i, s, 0); } void judge(){ for(int p = first[s]; ~p; p = edge[p].next) if(edge[p].w) return (void)(cout << "NO" << '\n'); cout << "YES" << '\n'; for(int i = 0; i < (m << 1); i += 2) { cout << low[(i >> 1) + 1] + edge[i ^ 1].w << '\n'; } cout << '\n'; } void clear() { tot = 0; memset(first, -1, sizeof(first)); memset(nece, 0, sizeof(nece)); } int main() { R(T); while(T--) { clear(); read(); dinic(); judge(); } return 0; } 
              
             
            
           
         
        
        
       
       
      
      
     
     

2、 有源汇可行流(有确定的源点和汇点,询问是否有可行的流量)

(1)解答:对于有源汇的网络流而言,它有一对点 s, t 的流量是不守恒的,所以我们不能直接用上文无源汇的方法做,那么,我们肯定可以考虑,如何将其转成我们能够解决的问题,其实很简单,我们从 t向 s连一条容量为 INF的边即可,之后直接跑无源汇可行流就好了。

(2)适用题目:

poj 2396

分析:裸的有源汇可行流,将每一个行,每一个列分别建成点,通过条件得出每一个点的上下界然后渐变,建立超级源汇 s, t 从 s 向每一个行的点连上下界均为当前行的和的边,从每一个列的点向 t 连一条上下界均为当前列的和的边, 然后中间直接以求出的每一个点的上下界建边,之后从 t到 s连容量为 INF的边, 然后添加附加源汇跑无源汇可行流即可。 (PS:建图呵呵 )

Source:

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p = false; c = getchar(); } while(isdigit(c)) v = (v << 3) + (v << 1) + (c ^ '0'), c = getchar(); if(!p) v = -v; } const int INF = 1e7; const int MAXN = 4000 + 30; const int MAXM = MAXN * 100; int tot, s, t, n, m, x, y, z, c, T, ss, tt; int first[MAXN], dis[MAXN], temp[MAXN], nece[MAXN], low[MAXM]; int map[2][210][23]; bool flag; struct node { int next, to, w; } edge[MAXM << 1]; inline void create(int x, int y, int w) { edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; edge[tot].w = w; tot++; } bool bfs(int s, int t) { memset(dis, -1, (t + 5 << 2)); memcpy(temp, first, (t + 5 << 2)); queue 
             
               q; q.push(s), dis[s] = 0; while(!q.empty()) { register int j = q.front(); q.pop(); for(register int p = first[j]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] == -1) { dis[edge[p].to] = dis[j] + 1; q.push(edge[p].to); if(edge[p].to == t) return true; } } } return false; } int dfs(int cur, int low, int t) { if(cur == t) return low; register int delta, res = 0; for(register int &p = temp[cur]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] > dis[cur]) { delta = dfs(edge[p].to, min(edge[p].w, low - res), t); if(delta) { edge[p].w -= delta, edge[p ^ 1].w += delta; res += delta; if(res == low) break; } } } if(res != low) dis[cur] = -1; return res; } void dinic(int s, int t) { register int ans = 0; while(bfs(s, t)) dfs(s, INF * 20, t); } void read() { flag = true; R(m), R(n), s = 0, t = n + m + 1, ss = n + m + 2, tt = n + m + 3; for(int i = 1; i <= m; ++i) R(low[i]), nece[i] += low[i], nece[s] -= low[i]; for(int i = m + 1; i <= n + m; ++i) R(low[i]), nece[i] -= low[i], nece[t] += low[i]; R(c); for(int i = 1; i <= m; ++i) for(int j = 1; j <= n; ++j) map[0][i][j] = 0, map[1][i][j] = INF; int x1, x2, y1, y2; char s1; while(c--) { R(x), R(y); if(!x) x1 = 1, x2 = m; else x1 = x2 = x; if(!y) y1 = 1, y2 = n; else y1 = y2 = y; scanf("%c", &s1), R(z); //cout << s << '\n'; for(int i = x1; i <= x2; ++i) for(int j = y1; j <= y2; ++j) { if(s1 == '>') map[0][i][j] = max(map[0][i][j], z + 1); if(s1 == '<') map[1][i][j] = min(map[1][i][j], z - 1); if(s1 == '=') map[0][i][j] = max(map[0][i][j], z); if(s1 == '=') map[1][i][j] = min(map[1][i][j], z); } } for(int i = 1; i <= m; ++i) for(int j = 1; j <= n; ++j) { if(map[1][i][j] < map[0][i][j]) return flag = false, (void)(cout << "IMPOSSIBLE" << '\n'); nece[i] -= map[0][i][j], nece[m + j] += map[0][i][j]; create(i, m + j, map[1][i][j] - map[0][i][j]), create(m + j, i, 0); } create(t, s, INF * 10), create(s, t, 0); for(int i = s; i <= t; ++i) { if(nece[i] >= 0) create(ss, i, nece[i]), create(i, ss, 0); else create(i, tt, -nece[i]), create(tt, i, 0); } } void judge() { for(int p = first[ss]; ~p; p = edge[p].next) if(edge[p].w) return (void)(cout << "IMPOSSIBLE" << '\n'); for(int i = 1; i <= m; ++i) for(int p = first[i]; ~p; p = edge[p].next) if(edge[p].to > m && edge[p].to < t) { //cout << edge[p].w << '\n'; map[0][i][edge[p].to - m] += edge[p ^ 1].w; } for(int i = 1; i <= m; ++i) { for(int j = 1; j <= n; ++j) cout << map[0][i][j] << " "; cout << '\n'; } cout << '\n'; } void clear() { tot = 0; memset(first, -1, sizeof(first)); memset(nece, 0, sizeof(nece)); } int main() { R(T); while(T--) { clear(); read(); if(!flag) continue; dinic(ss, tt); judge(); } return 0; } 
              
             
            
           
         
        
        
       
       
      
      
     
     

3、有源汇最大流(有确定源汇点,求解最大流)

(1) 解答:如果从 s 到 t 有一个流量为 a 的可行流 f,那么从 t->s 连一条弧 (t, s), 其流量下界 B(t, s) = a, 则这个图一定有一个无源汇的可行流:除了弧 (t, s)的容量为 a 外,其余边的容量与 f 相同。如果从 s 到 t 的最大流量为 amax, 那么从 t 到 s 连一条下界 B(t, s) = a> amax 的弧 (t, s), 则从在这个改造后的图中一定没有无源汇的可行流:否则将这个可行流中的弧 (t, s)除去,就得到了原图中 s 到 t 的流量为 a的流,大于最大流量 amax, 产生矛盾。如果给定一个参数 a,如何判断在 G 中从 s 到 t 是否有一个流量为 a 的可行流呢?综上所述,判断在 G 中是否有 a 的可行流和判断在改造后的图中是否有一个无源汇的可行流完全等价。因此,执行一次普通最大流算法,就可以完成这个任务了。下面回到问题中来,我们不妨二分枚举这个参数 a,每次改造图后执行一次最大流来判断是否有 s 到 t 的流量为 a 的可行流。这样找到 a 能取到的最大值,也就是 G 图中的最大流 amax 了。

(2)算法优化:上面的方法显然复杂度很高,先不说如果有小数流量可能直接死循,直接二分整数的复杂度乘上单次网络流复杂度就可以爆炸了。现在我们考虑优化。

我们直接连上原来的 t -> s 容量为INF, 然后用附加源汇 ss, tt跑一遍最大流, 然后判断是否为可行流,如果可行, 用原来的源汇点s, t 跑一遍最大流这一次所得的最大流就是原图的最大流。 (PS:证明我不会····· )

(3)适用题目:

zoj 3229

分析:直接根据题意建边即可,注意最后的输出顺序不要出问题即可。

Source:

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p = false; c = getchar(); } while(isdigit(c)) v = (v << 3) + (v << 1) + (c ^ '0'), c = getchar(); if(!p) v = -v; } const int INF = 1e7; const int MAXN = 1500 + 30; const int MAXM = MAXN * 100; int tot, s, t, n, m, x, y, z, a, b, c, ss, tt; int first[MAXN], dis[MAXN], temp[MAXN], nece[MAXN], low[MAXM], ans[MAXN]; int map[370][110], cnt[370][110]; struct node { int next, to, w; } edge[MAXM << 1]; inline void create(int x, int y, int w) { edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; edge[tot].w = w; tot++; } bool bfs(int s, int t) { memset(dis, -1, (t + 5 << 2)); memcpy(temp, first, (t + 5 << 2)); queue 
             
               q; q.push(s), dis[s] = 0; while(!q.empty()) { register int j = q.front(); q.pop(); for(register int p = first[j]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] == -1) { dis[edge[p].to] = dis[j] + 1; q.push(edge[p].to); if(edge[p].to == t) return true; } } } return false; } int dfs(int cur, int low, int t) { if(cur == t) return low; register int delta, res = 0; for(register int &p = temp[cur]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] > dis[cur]) { delta = dfs(edge[p].to, min(edge[p].w, low - res), t); if(delta) { edge[p].w -= delta, edge[p ^ 1].w += delta; res += delta; if(res == low) break; } } } if(res != low) dis[cur] = -1; return res; } int dinic(int s, int t) { register int ans = 0; while(bfs(s, t)) ans += dfs(s, INF * 20, t); return ans; } void read() { s = 0, t = n + m + 1, ss = t + 1, tt = t + 2; for(int i = n + 1; i <= n + m; ++i) { R(x), nece[t] += x, nece[i] -= x; create(i, t, INF - x), create(t, i, 0); } for(int i = 1; i <= n; ++i) { R(x), R(y); create(s, i, y), create(i, s, 0); for(int j = 1; j <= x; ++j) { R(a), R(b), R(c); map[i][j] = a; cnt[i][j] = b; a += (n + 1); nece[a] += b, nece[i] -= b; create(i, a, c - b), create(a, i, 0); } map[i][x + 1] = -1; } create(t, s, INF * 10), create(s, t, 0); for(int i = s; i <= t; ++i) if(nece[i] < 0) create(i, tt, -nece[i]), create(tt, i, 0); else create(ss, i, nece[i]), create(i, ss, 0); } void judge() { for(int p = first[ss]; ~p; p = edge[p].next) if(edge[p].w) return (void)(cout << "-1" << '\n'); cout << dinic(s, t) << '\n'; for(int i = 1; i <= n; ++i) { memset(ans, 0, (m + 10 << 2)); for(int p = first[i]; ~p; p = edge[p].next) if(edge[p].to > n && edge[p].to < t) ans[edge[p].to - n] += edge[p ^ 1].w; for(int j = 1; ~map[i][j]; ++j) cout << ans[map[i][j] + 1] + cnt[i][j] << '\n'; } //cout << '\n'; } void clear() { tot = 0; memset(first, -1, sizeof(first)); memset(nece, 0, sizeof(nece)); } int main() { while(~scanf("%d%d", &n, &m)) { clear(); read(); dinic(ss, tt); judge(); } return 0; } 
              
             
            
           
         
        
        
       
       
      
      
     
     

4、有源汇最小流(有确定源汇点,求解最小流)

(1) 解答:求解最大流我们使用的是二分下界,现在我们可以直接更改成二分上界即可。

(2)算法优化:先不连 t->s的边用附加源汇点 ss, tt 跑一遍最大流,之后连接 t->s 容量为 INF,再用附加源汇 ss, tt 跑一遍最大流, 然后用上面的方法判断是否为可行流,如果可行则, t->s的反向边的流量为当前最小流(证明还是不会······)

(3)适用题目:

hdu3157

分析:根据题意建边,裸跑有源汇最小流即可。

Source:

#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
         
         
#include 
         
         
           #include 
          
            #include 
           
             #include 
            
              using namespace std; inline void R(int &v) { char c = 0; bool p = true; v = 0; while(!isdigit(c)) { if(c == '-') p =false; c = getchar(); } while(isdigit(c)) v = (v << 3) + (v << 1) + (c ^ '0'), c = getchar(); if(!p) v = -v; } int get(char *x) { int len = strlen(x), v = 0; for(int i = 0; i < len; ++i) v = (v << 3) + (v << 1) + (x[i] ^ '0'); return v; } const int MAXN = 100; const int MAXM = 10000; const int INF = 1e7; int n, m, s, t, ss, tt, u, v, w, tot; int first[MAXN], dis[MAXN], temp[MAXN], nece[MAXN]; char x[10], y[10], z; struct node { int next, to, w; } edge[MAXM << 1]; inline void create(int x, int y, int z) { edge[tot].next = first[x]; first[x] = tot; edge[tot].to = y; edge[tot].w = z; tot++; } void read() { s = 0, t = n + 1, ss = t + 1, tt = t + 2; for(int i = 1; i <= m; ++i) { scanf("%s%s", x, y), R(w); u = (x[0] == '+') ? s : (get(x)); v = (y[0] == '-') ? t : (get(y)); nece[u] -= w, nece[v] += w; create(u, v, INF - w), create(v, u, 0); } for(int i = s; i <= t; ++i) if(nece[i] < 0) create(i, tt, -nece[i]), create(tt, i, 0); else create(ss, i, nece[i]), create(i, ss, 0); } bool bfs(int s, int t) { memset(dis, -1, t + 5 << 2); memcpy(temp, first, t + 5 << 2); queue 
             
               q; dis[s] = 0, q.push(s); while(!q.empty()) { int j = q.front(); q.pop(); for(int p = first[j]; ~p; p = edge[p].next) { if(edge[p].w && dis[edge[p].to] == -1) { dis[edge[p].to] = dis[j] + 1; q.push(edge[p].to); if(edge[p].to == t) return true; } } } return false; } int dfs(int cur, int low, int t) { if(cur == t) return low; int delta, res = 0; for(int &p = temp[cur]; ~p; p = edge[p].next) { if(edge[p].w && dis[cur] < dis[edge[p].to]) { delta = dfs(edge[p].to, min(low - res, edge[p].w), t); if(delta) { edge[p].w -= delta, edge[p ^ 1].w += delta; res += delta; if(res == low) break; } } } if(res != low) dis[cur] = -1; return res; } void dinic(int s, int t) { while(bfs(s, t)) dfs(s, INF, t); } void judge() { dinic(ss, tt); int mark = tot; create(t, s, INF), create(s, t, 0), dinic(ss, tt); for(int p = first[ss]; ~p; p = edge[p].next) if(edge[p].w) return (void)(cout << "impossible" << '\n'); cout << edge[mark ^ 1].w << '\n'; } void clear() { tot = 0; memset(first, -1, sizeof(first)); memset(nece, 0, sizeof(nece)); } int main() { while(true) { R(n), R(m); if(n == 0 && m == 0) break; clear(); read(); judge(); } return 0; } 
              
             
            
           
         
        
        
       
       
      
      
     
     

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值