【网络流专练一】UVA五题(UVA12125,UVA11082,UVA10983,UVA1306,UVA10735)
網絡流專練
- March of the Penguins
- 矩陣解壓 Matrix Decompressing
- Buy one, get the rest free
- The K-League
- 混合圖的歐拉回路 Euler Circuit
什么破網站,多余空格換行都不能有,還nm不報PE/RE只報WA。
少一行換行也不行,這是什么垃圾文本比較。
March of the Penguins
problem
UVA12125
solution
枚舉冰塊,然后判斷被枚舉冰塊是否符合條件。
將冰塊拆成兩個點,左點和右點,之間流量是可跳躍次數。
超級源點和左點連邊,流量為該冰塊上的初始企鵝數。
然后兩兩冰塊枚舉能否互跳,分別右點連向左點,流量無窮。
最后連一條最后的冰塊左點和超級匯點的邊,流量無窮。
code
#include <cmath> #include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define maxn 205 #define maxm 40005 #define inf 0x7f7f7f7f queue < int > q; struct node { int to, nxt, flow, ori; }E[maxm]; int T, n, cnt, tot, s, t; double d; double x[maxn], y[maxn]; int dep[maxn], cur[maxn], head[maxn], num[maxn], lim[maxn];double dis( int i, int j ) { return sqrt( ( x[i] - x[j] ) * ( x[i] - x[j] ) + ( y[i] - y[j] ) * ( y[i] - y[j] ) ); }void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w, w };head[u] = cnt ++;E[cnt] = { u, head[v], 0, 0 };head[v] = cnt ++; }bool bfs() {for( int i = 0;i <= t;i ++ ) dep[i] = 0;dep[s] = 1; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = cur[u] = head[u];~ i;i = E[i].nxt ) {int v = E[i].to; if( ! dep[v] and E[i].flow ) {dep[v] = dep[u] + 1;q.push( v );}}}return dep[t]; }int dfs( int u, int cap ) {if( u == t or ! cap ) return cap;int flow = 0;for( int i = cur[u];~ i;i = E[i].nxt ) {cur[u] = i; int v = E[i].to;if( dep[v] == dep[u] + 1 ) {int w = dfs( v, min( cap, E[i].flow ) );if( ! w ) continue;E[i ^ 1].flow += w;E[i].flow -= w;flow += w;cap -= w;if( ! cap ) break;}}return flow; }int dinic() {int ans = 0;while( bfs() ) ans += dfs( s, inf );return ans; }void build( int now ) {memset( head, -1, sizeof( head ) ); cnt = 0;for( int i = 1;i <= n;i ++ ) {addedge( s, i, num[i] );addedge( i, i + n, lim[i] );for( int j = i + 1;j <= n;j ++ )if( dis( i, j ) <= d ) {addedge( i + n, j, inf );addedge( j + n, i, inf );}}addedge( now, t, inf ); }int main() {scanf( "%d", &T );while( T -- ) {memset( head, -1, sizeof( head ) );cnt = tot = 0;scanf( "%d %lf", &n, &d );t = n << 1 | 1;for( int i = 1;i <= n;i ++ ) {scanf( "%lf %lf %d %d", &x[i], &y[i], &num[i], &lim[i] );tot += num[i];}bool flag = 0;for( int i = 1;i <= n;i ++ ) {build( i );if( dinic() == tot ) {if( flag ) printf( " " );printf( "%d", i - 1 );flag = 1;}}if( ! flag ) printf( "-1" ); printf( "\n" );}return 0; }矩陣解壓 Matrix Decompressing
problem
UVA11082
solution
顯然可以解出每行每列的和。
然后將行列分成左右點兩部分(類似二分圖)。
超級源點和行連邊,流量為行的和。
列和超級匯點兩邊,流量為列的和。
然后兩兩行列點之間流量為 191919。
當超級源點和超級匯點的邊都滿流時,iii 行 jjj 列之間的邊流過的流量 +1+1+1 就是 a[i,j]a[i,j]a[i,j] 的值。
解釋:
- 每個數大小在 [1,20][1,20][1,20],所以不用太大。
- 可能出現零流的狀況(因為同是最大流但流法不太一樣導致有些邊代表的位置值計算出來是 000,不符合范圍)所以提前 ?1-1?1。
code
#include <bits/stdc++.h> using namespace std; #define maxn 100 #define inf 0x7f7f7f7f struct node { int to, nxt, flow; }E[maxn * maxn]; int T, n, m, s, t, cnt; int r[maxn], c[maxn], head[maxn], cur[maxn], dep[maxn]; int ans[maxn][maxn];queue < int > q;void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++;E[cnt] = { u, head[v], 0 };head[v] = cnt ++; }bool bfs() {memset( dep, 0, sizeof( dep ) );dep[s] = 1; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = cur[u] = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( ! dep[v] and E[i].flow ) {dep[v] = dep[u] + 1;q.push( v );}}}return dep[t]; }int dfs( int u, int cap ) {if( u == t or ! cap ) return cap;int flow = 0;for( int i = cur[u];~ i;i = E[i].nxt ) {cur[u] = i; int v = E[i].to;if( dep[v] == dep[u] + 1 ) {int w = dfs( v, min( cap, E[i].flow ) );if( ! w ) continue;E[i ^ 1].flow += w;E[i].flow -= w;flow += w;cap -= w;if( ! cap ) break;}}return flow; }int main() {scanf( "%d", &T );for( int Case = 1;Case <= T;Case ++ ) {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) scanf( "%d", &r[i] );for( int i = 1;i <= m;i ++ ) scanf( "%d", &c[i] );for( int i = n;i;i -- ) r[i] = r[i] - r[i - 1];for( int i = m;i;i -- ) c[i] = c[i] - c[i - 1];cnt = s = 0, t = n + m + 1;memset( head, -1, sizeof( head ) );for( int i = 1;i <= n;i ++ ) addedge( s, i, r[i] - m );for( int i = 1;i <= m;i ++ ) addedge( i + n, t, c[i] - n );for( int i = 1;i <= n;i ++ ) for( int j = 1;j <= m;j ++ ) addedge( i, j + n, 19 );while( bfs() ) dfs( s, inf );for( int i = 1;i <= n;i ++ ) for( int j = head[i];~ j;j = E[j].nxt ) ans[i][E[j].to - n] = 20 - E[j].flow;printf( "Matrix %d\n", Case );for( int i = 1;i <= n;i ++ ) { for( int j = 1;j <= m;j ++ ) printf( "%d ", ans[i][j] ); printf( "\n" ); }}return 0; }Buy one, get the rest free
problem
UVA10983
solution
觀察到總天數 d≤10d\le 10d≤10,非常小,很套路的拆點。
將一個城市拆成 ddd 個點,表示第 ddd 天的城市。
一架航班就連對應兩座城的兩個時間代表點。
一座城市之間也有天數小的與天數大的點連邊。
超級源點和每座城市的第 000 點代表點連邊,流量為該城市人數。
code
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define maxn 2005 #define inf 0x7f7f7f7f struct node { int u, v, c, p, e; }flight[maxn]; struct edge { int to, nxt, flow; }E[maxn << 3]; int T, n, m, d, cnt, tot, s, t; int head[maxn], dep[maxn], cur[maxn], w[maxn]; queue < int > q;void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++;E[cnt] = { u, head[v], 0 };head[v] = cnt ++; }bool bfs() {memset( dep, 0, sizeof( dep ) );dep[s] = 1; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = cur[u] = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( ! dep[v] and E[i].flow ) {dep[v] = dep[u] + 1;q.push( v );}}}return dep[( d + 1 ) * n]; }int dfs( int u, int cap ) {if( u == n * ( d + 1 ) or ! cap ) return cap;int flow = 0;for( int i = cur[u];~ i;i = E[i].nxt ) {cur[u] = i; int v = E[i].to;if( dep[v] == dep[u] + 1 ) {int w = dfs( v, min( cap, E[i].flow ) );if( ! w ) continue;E[i ^ 1].flow += w;E[i].flow -= w;flow += w;cap -= w;if( ! cap ) break;}}return flow; }int dinic() {int ans = 0;while( bfs() ) ans += dfs( s, inf );return ans; }bool check( int pos ) {memset( head, -1, sizeof( head ) ); cnt = 0;for( int i = 1;i <= n;i ++ ) {addedge( s, i, w[i] );for( int j = 0;j < d;j ++ )addedge( i + j * n, i + ( j + 1 ) * n, inf );}for( int i = 1;i <= pos;i ++ ) {int u = flight[i].u, v = flight[i].v, c = flight[i].c, e = flight[i].e;addedge( u + e * n, v + ( e + 1 ) * n, c );}return dinic() == tot; }int main() {scanf( "%d", &T );for( int Case = 1;Case <= T;Case ++ ) {scanf( "%d %d %d", &n, &d, &m );for( int i = 1, u, v, c, p, e;i <= m;i ++ ) {scanf( "%d %d %d %d %d", &u, &v, &c, &p, &e );flight[i] = { u, v, c, p, e };}tot = 0;for( int i = 1;i <= n;i ++ ) scanf( "%d", &w[i] ), tot += w[i];sort( flight + 1, flight + m + 1, []( node x, node y ) { return x.p < y.p; } );int l = 1, r = m, ans = -1;while( l <= r ) {int mid = ( l + r ) >> 1;if( check( mid ) ) ans = flight[mid].p, r = mid - 1;else l = mid + 1;}if( ~ ans ) printf( "Case #%d: %d\n", Case, ans );else printf( "Case #%d: Impossible\n", Case );}return 0; }The K-League
problem
UVA1306
solution
枚舉每個隊伍成為最后的冠軍,然后欽定其剩余的比賽肯定都是這個隊伍贏。
然后就是剩下隊伍之間的比賽。
比賽建立 n?nn*nn?n 個點(兩個隊伍之間的比賽),隊伍 nnn 個點。
超級源點和比賽連邊,流量為兩個隊伍之間要進行的比賽場數。
比賽和比賽的兩個隊伍連邊,流量為無窮。
隊伍和超級匯點連邊,流量為冠軍隊伍贏的場數減去本隊伍已經贏的場數(保證所有隊伍贏的場數不能超過冠軍隊伍)。
當超級源點的邊都滿流時,證明合法。
#include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define maxn 700 #define inf 0x7f7f7f7f int T, n, cnt, s, t; int w[maxn], d[maxn], head[maxn], dep[maxn], cur[maxn]; int a[maxn][maxn]; struct node { int to, nxt, flow; }E[maxn * maxn]; queue < int > q;void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++;E[cnt] = { u, head[v], 0 };head[v] = cnt ++; }int build( int x ) {memset( head, -1, sizeof( head ) ); cnt = 0;int tot = w[x];for( int i = 1;i <= n;i ++ ) tot += a[x][i];for( int i = 1;i <= n;i ++ ) if( w[i] > tot ) return -1;for( int i = 1;i <= n;i ++ )for( int j = i + 1;j <= n;j ++ )if( i ^ x and j ^ x and a[i][j] ) {addedge( s, ( i - 1 ) * n + j, a[i][j] );addedge( ( i - 1 ) * n + j, i + n * n, inf );addedge( ( i - 1 ) * n + j, j + n * n, inf );}for( int i = 1;i <= n;i ++ ) if( i ^ x ) addedge( i + n * n, t, tot - w[i] );return tot - w[x]; }bool bfs() {memset( dep, 0, sizeof( dep ) );dep[s] = 1; q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = cur[u] = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( ! dep[v] and E[i].flow ) {dep[v] = dep[u] + 1;q.push( v );}}}return dep[t]; }int dfs( int u, int cap ) {if( u == t or ! cap ) return cap;int flow = 0;for( int i = cur[u];~ i;i = E[i].nxt ) {cur[u] = i; int v = E[i].to;if( dep[v] == dep[u] + 1 ) {int w = dfs( v, min( cap, E[i].flow ) );if( ! w ) continue;E[i ^ 1].flow += w;E[i].flow -= w;flow += w;cap -= w;if( ! cap ) break;}}return flow; }int dinic() {int ans = 0;while( bfs() ) ans += dfs( s, inf );return ans; }int main() {scanf( "%d", &T );while( T -- ) {scanf( "%d", &n ); s = 0, t = n * ( n + 1 ) + 1;for( int i = 1;i <= n;i ++ ) scanf( "%d %d", &w[i], &d[i] );int tot = 0;for( int i = 1;i <= n;i ++ ) for( int j = 1;j <= n;j ++ ) scanf( "%d", &a[i][j] ), tot += a[i][j];tot >>= 1;for( int i = 1, flag = 0;i <= n;i ++ ) {int win = build( i );if( win == -1 ) continue;if( dinic() + win == tot ) {if( flag ) printf( " %d", i );else printf( "%d", i );flag = 1;}}printf( "\n" );}return 0; }
混合圖的歐拉回路 Euler Circuit
problem
UVA10735
solution
歐拉回路的條件就是出邊數等于入邊數。
不妨設 di:id_i:idi?:i 點的出邊減去入邊的數量。
當所有的 ddd 都為 000 的時候,證明是一個歐拉回路。
給所有無向邊隨意欽定一個方向。
改變一條邊的順序就是一個點度數 ?1/+1-1/+1?1/+1 的區別。
可以看作是流量 111 的流通。
把 d>0d>0d>0 的歸入左邊點,d<0d<0d<0 的歸入右邊點。
超級源點和左邊點連邊,流量為左邊點多出的數量。
右邊點和超級匯點連邊,流量為右邊點少掉的數量。
無向邊就成為左邊點和右邊點之間流量為 111 的邊。
當超級源點超級匯點都滿流時,證明歐拉回路出現。
最后根據左右點之間邊流量為 1/01/01/0 判斷方向是否改變,輸出這個歐拉回路即可。
code
#include <bits/stdc++.h> using namespace std; #define maxn 2000 #define inf 0x7f7f7f7f struct node { int to, nxt, flow; }E[maxn]; pair < int, int > edge[maxn]; vector < int > G[maxn]; queue < int > q; int n, m, cnt, S, T, sum; int head[maxn], cur[maxn], dep[maxn], d[maxn], id[maxn];void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++;E[cnt] = { u, head[v], 0 };head[v] = cnt ++; }bool bfs() {memset( dep, 0, sizeof( dep ) );dep[S] = 1; q.push( S );while( ! q.empty() ) {int u = q.front(); q.pop();for( int i = cur[u] = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( ! dep[v] and E[i].flow ) {dep[v] = dep[u] + 1;q.push( v );}}}return dep[T]; }int dfs( int u, int cap ) {if( u == T or ! cap ) return cap;int flow = 0;for( int i = cur[u];~ i;i = E[i].nxt ) {cur[u] = i; int v = E[i].to;if( dep[v] == dep[u] + 1 ) {int w = dfs( v, min( cap, E[i].flow ) );if( ! w ) continue;E[i ^ 1].flow += w;E[i].flow -= w;flow += w;cap -= w;if( ! cap ) break;}}return flow; }int dinic() {int ans = 0;while( bfs() ) ans += dfs( S, inf );return ans; }void dfs( int u ) {while( ! G[u].empty() ) {int v = G[u].back();G[u].pop_back();dfs( v );printf( " %d", u );} }bool init() {scanf( "%d %d", &n, &m );cnt = sum = S = 0, T = n + 1;memset( head, -1, sizeof( head ) );memset( d, 0, sizeof( d ) );for( int i = 1;i <= n;i ++ ) G[i].clear();for( int i = 1, u, v;i <= m;i ++ ) {char ch[5];scanf( "%d %d %S", &u, &v, ch );edge[i] = { u, v };d[u] ++, d[v] --;if( ch[0] == 'U' ) id[i] = cnt, addedge( u, v, 1 );else id[i] = -1;}for( int i = 1;i <= n;i ++ ) {if( d[i] & 1 ) return 0;d[i] >>= 1;if( d[i] > 0 ) sum += d[i], addedge( S, i, d[i] );else addedge( i, T, -d[i] );}return 1; }void solve() {if( dinic() != sum ) { printf( "No euler circuit exist\n" ); return; }#define u edge[i].first#define v edge[i].secondfor( int i = 1;i <= m;i ++ )if( id[i] >= 0 and ! E[id[i]].flow ) G[u].push_back( v );else G[v].push_back( u );#undef u#undef vprintf( "1" );dfs( 1 );puts( "" ); }int main() {int T;scanf( "%d", &T );while( T -- ) {if( ! init() ) printf( "No euler circuit exist\n" );else solve();if( T ) printf( "\n" );}return 0; }總結
以上是生活随笔為你收集整理的【网络流专练一】UVA五题(UVA12125,UVA11082,UVA10983,UVA1306,UVA10735)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 12.4 寸 1759 元大促:小米平板
- 下一篇: 【无码专区5】01串(大讨论+构造)