图论专题I
[pku2396 Budget]带源汇的上下界最大流
From:http://poj.org/problem?id=2396
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <queue> #include <stack> #include <functional> using namespace std; #define N 205+25 #define M 25 #define oo 0x6fffffff #define nil (-1) int cap[N][N], flow[N][N], d[N], fa[N], low[N][N], high[N][N], in[N], out[N], maxflow, _S, _T, S, T; void Init(int n, int m){ S = 0, T = n+m+1; _S = n+m+2; _T = n+m+3; memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); memset(flow, 0, sizeof(flow)); memset(cap, 0, sizeof(cap)); memset(low, 0, sizeof(low)); memset(high, 0, sizeof(high)); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) high[i][j+n] = oo; } bool Bfs(int s, int t, int n){ for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil; queue<int> Q; d[s] = 0; Q.push(s); while ( !Q.empty() ){ int u = Q.front(); Q.pop(); for (int v = 0; v <= n; ++v){ if ( cap[u][v] > 0 && d[v] == oo ){ d[v] = d[u] + 1; fa[v] = u; if ( v == t ) return true; Q.push(v); } } } return false; } int Dfs(int u, int s, int t, int n, int& maxflow){ if ( u != t ){ for (int v = 0; v <= n; ++v){ if ( cap[u][v] > 0 && d[v] == d[u] + 1 ){ int r = Dfs(v, s, t, n, maxflow); if ( r != nil && r != u ) return r; } } d[u] = oo; } else { int cf = oo, r = fa[t]; for (int i = t; i != s; i = fa[i]) cf = min(cf, cap[fa[i]][i]); maxflow += cf; for (int i = t; i != s; i = fa[i]){ cap[fa[i]][i] -= cf; cap[i][fa[i]] += cf; flow[fa[i]][i] += cf; flow[i][fa[i]] -= cf; if ( !cap[fa[i]][i] ) r = fa[i]; } return r; } return nil; } int Dinic(int s, int t, int n){ int maxflow = 0; while ( Bfs(s, t, n) ) Dfs(s, s, t, n, maxflow); return maxflow; } void Run(int n, int m){ int sum = 0; for (int i = 0; i <= n+m+1; ++i){ for (int j = 0; j <= n+m+1; ++j){ cap[i][j] = high[i][j] - low[i][j]; in[j] += low[i][j]; out[i] += low[i][j]; } } for (int i = 0; i <= n+m+1; ++i){ int diff = in[i] - out[i]; if ( diff >= 0 ) cap[_S][i] = diff, sum += diff; else if ( diff < 0 ) cap[i][_T] = -diff; } cap[T][S] = oo; if ( Dinic(_S, _T, n+m+3) != sum ) { puts("IMPOSSIBLE\n"); return; } cap[T][S] = cap[S][T] = 0; Dinic(S, T, n+m+1); for (int i = 1; i <= n; ++i){ for (int j = 1; j <= m; ++j){ printf("%d ", flow[i][j+n]+low[i][j+n]); } printf("\n"); } } int main() { int n,m,ca; scanf("%d", &ca); while ( ca-- ){ scanf("%d%d", &n, &m); Init(n, m); int dd; for (int i = 1; i <= n; ++i){ scanf("%d", &dd); low[S][i] = high[S][i] = dd; } for (int i = 1; i <= m; ++i){ scanf("%d", &dd); low[i+n][T] = high[i+n][T] = dd; } int cc; scanf("%d", &cc); while ( cc-- ){ char op; int I, J; scanf("%d %d %c %d", &I, &J, &op, &dd); if ( !I && J != 0 ){ for (int i = 1; i <= n; ++i){ if ( '<' == op ) high[i][J+n] = min(high[i][J+n], dd-1); else if ( '>' == op ) low[i][J+n] = max(low[i][J+n], dd+1); else if ( '=' == op ) high[i][J+n] = low[i][J+n] = dd; } } else if ( I != 0 && !J ){ for (int j = 1; j <= m; ++j){ if ( '<' == op ) high[I][j+n] = min(high[I][j+n], dd-1); else if ( '>' == op ) low[I][j+n] = max(low[I][j+n], dd+1); else if ( '=' == op ) high[I][j+n] = low[I][j+n] = dd; } } else if ( !I && !J ){ for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j){ if ( '<' == op ) high[i][j+n] = min(high[i][j+n], dd-1); else if ( '>' == op ) low[i][j+n] = max(low[i][j+n], dd+1); else if ( '=' == op ) high[i][j+n] = low[i][j+n] = dd; } } else { if ( '<' == op ) high[I][J+n] = min(high[I][J+n], dd-1); else if ( '>' == op ) low[I][J+n] = max(low[I][J+n], dd+1); else if ( '=' == op ) high[I][J+n] = low[I][J+n] = dd; } } Run(n, m); printf("\n"); } return 0; }
[zoj2314 Reactor Cooling]无源汇的上下界可行流
From:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <queue>
#include <stack>
#include <functional>
using namespace std;
#define N 205
#define M 25
#define oo 0x6fffffff
#define nil (-1)
queue<pair<int,int> > E;
int cap[N][N], flow[N][N], d[N], fa[N], low[N][N], high[N][N], in[N], out[N], _S, _T, maxflow;
void Init(int n){
_S = 0, _T = n;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= n; ++j){
in[i] = out[i] = 0;
cap[i][j] = 0; flow[i][j] = 0;
low[i][j] = oo; high[i][j] = oo;
}
while ( !E.empty() ) E.pop();
}
bool Bfs(int n){
for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil;
queue<int> Q;
d[_S] = 0; Q.push(_S);
while ( !Q.empty() ){
int u = Q.front(); Q.pop();
for (int v = 0; v <= n; ++v){
if ( cap[u][v] > 0 && d[v] == oo ){
d[v] = d[u] + 1; fa[v] = u;
if ( v == _T ) return true;
Q.push(v);
}
}
}
return false;
}
int Dfs(int u, int n){
if ( u != _T ){
if ( d[u] != oo ){
for (int v = 0; v <= n; ++v){
if ( cap[u][v] > 0 && d[v] == d[u] + 1 ){
int r = Dfs(v, n);
if ( r != nil && r != u ) return r;
}
}
d[u] = oo;
}
} else {
int cf = oo, r = fa[_T];
for (int i = _T; i != _S; i = fa[i]) cf = min(cf, cap[fa[i]][i]);
maxflow += cf;
for (int i = _T; i != _S; i = fa[i]){
cap[fa[i]][i] -= cf; cap[i][fa[i]] += cf;
flow[fa[i]][i] += cf; flow[i][fa[i]] -= cf;
if ( !cap[fa[i]][i] ) r = fa[i];
}
return r;
}
return nil;
}
int Dinic(int n){
maxflow = 0;
while ( Bfs(n) ) Dfs(_S, n);
return maxflow;
}
void Run(int n){
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j){
if ( high[i][j] == oo || low[i][j] == oo ) continue;
cap[i][j] = high[i][j] - low[i][j];
in[j] += low[i][j]; out[i] += low[i][j];
}
int sum = 0;
for (int i = 1; i <= n; ++i){
int diff = in[i] - out[i];
if ( diff > 0 ) cap[_S][i] = diff, sum += diff;
else if ( diff < 0 ) cap[i][_T] = -diff;
}
if ( sum != Dinic(n+1) ){
printf("NO\n");
} else {
printf("YES\n");
while ( !E.empty() ){
pair<int,int> e = E.front(); E.pop();
printf("%d\n", flow[e.first][e.second]+low[e.first][e.second]);
}
}
}
int main()
{
int n,m, ca;
scanf("%d", &ca);
while ( ca-- ){
scanf("%d%d", &n, &m);
Init(n+1);
for (int i = 0; i < m; ++i){
int I, J, l, c;
scanf("%d%d%d%d", &I, &J, &l, &c);
low[I][J] = l; high[I][J] = c;
E.push(make_pair(I,J));
}
Run(n);
printf("\n");
}
return 0;
}
[noi 志愿者招募]带源汇的上下界费用流
From:http://www.lydsy.com/JudgeOnline/problem.php?id=1061
Solution:
差分, 流守恒性, 容量限制(https://www.byvoid.com/blog/noi-2008-employee)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <queue>
#include <stack>
#include <functional>
using namespace std;
#define N 1005
#define M 10005
#define oo 0x7fffffff
#define nil (-1)
bool inQ[N];
struct { int w,c; int to,ba,next; } nod[N*2+2*M];
int adj[N], d[N], fa[N], idx[N], in[N], now, S, T;
void Init(int n, int m){
now = 0;
S = 0; T = n+2;
for (int i = 0 ; i <= n+2; ++i) adj[i] = nil, in[i] = 0;
}
int NewNode(int u, int w, int c){
nod[++now].to = u; nod[now].w = w; nod[now].c = c; nod[now].next = nil;
return now;
}
void AddEdge(int u, int v, int w, int c){
int u1 = NewNode(v, w, c);
int v1 = NewNode(u, -w, 0);
nod[u1].ba = v1; nod[v1].ba = u1;
nod[u1].next = adj[u]; adj[u] = u1;
nod[v1].next = adj[v]; adj[v] = v1;
}
bool Spfa(int s, int t, int n){
for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil, inQ[i] = false;
queue<int> Q;
d[s] = 0; Q.push(s); inQ[s] = true;
while ( !Q.empty() ){
int u = Q.front(); Q.pop(); inQ[u] = false;
for (int i = adj[u]; i != nil; i = nod[i].next){
int v = nod[i].to;
if ( nod[i].c > 0 && d[v] - d[u] > nod[i].w ){
d[v] = d[u] + nod[i].w; fa[v] = u; idx[v] = i;
if ( !inQ[v] ) inQ[v] = true, Q.push(v);
}
}
}
return d[t] != oo;
}
int Argument(int s, int t){
int cf = oo;
for (int i = t; i != s; i = fa[i]) cf = min(cf, nod[idx[i]].c);
for (int i = t; i != s; i = fa[i]){
nod[idx[i]].c -= cf; nod[nod[idx[i]].ba].c += cf;
}
return cf * d[t];
}
int MinCost_Flow(int s, int t, int n){
int ans = 0;
while ( Spfa(s, t, n) ) ans += Argument(s, t);
return ans;
}
void RunTest(int n, int m){
Init(n, m);
for (int i = 1; i <= n; ++i){
int cc;
scanf("%d", &cc);
in[i] -= cc; in[i+1] += cc;
AddEdge(i, i+1, 0, oo-cc);
}
for (int i = 1; i <= m; ++i){
int sd, td, cc;
scanf("%d%d%d", &sd, &td, &cc);
AddEdge(td+1, sd, cc, oo);
}
for (int i = 1; i <= n+1; ++i){
if ( in[i] > 0 ) AddEdge(S, i, 0, in[i]);
else AddEdge(i, T, 0, -in[i]);
}
printf("%d\n", MinCost_Flow(S, T, n+2));
}
int main()
{
int n, m;
while ( scanf("%d%d", &n, &m) != EOF ){
RunTest(n, m);
}
return 0;
}
[vijos 80人环游世界]带源汇的上下界费用流
From:https://www.vijos.org/p/1213
Solution:
这题把我快搞残了,最容易想到的是拆点,然后容量上下界为V; 然后, 需要注意的两个点:
1.每个人都可能从任何一个城市出发, 所以要用一个点S连接所有城市, 容量为oo,再用一个超级源点SS连接S,容量为m。
2.最终m个人可能停留在M(M<=m)个城市, 所以还要把停留在的城市的流量引导超级汇点TT去。
综合1,2及容量上下界, 就变成m个流量走到汇点并保证m个城市恰好经过。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <queue>
#include <stack>
#include <functional>
using namespace std;
#define N 205
#define M 10005
#define oo 0x7fffffff
#define nil (-1)
bool inQ[N];
struct { int w,c; int to,ba,next; } nod[N*N];
int adj[N], d[N], fa[N], idx[N], in[N], now, S, T, _S, _T;
void Init(int n, int m){
now = 0;
S = 2*n+1; T = 2*n+2; _S = 2*n+3; _T = 2*n+4;
for (int i = 0 ; i <= 2*n+4; ++i) adj[i] = nil, in[i] = 0;
}
int NewNode(int u, int w, int c){
nod[++now].to = u; nod[now].w = w; nod[now].c = c; nod[now].next = nil;
return now;
}
void AddEdge(int u, int v, int w, int c){
int u1 = NewNode(v, w, c);
int v1 = NewNode(u, -w, 0);
nod[u1].ba = v1; nod[v1].ba = u1;
nod[u1].next = adj[u]; adj[u] = u1;
nod[v1].next = adj[v]; adj[v] = v1;
}
bool Spfa(int s, int t, int n){
for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil, inQ[i] = false;
queue<int> Q;
d[s] = 0; Q.push(s); inQ[s] = true;
while ( !Q.empty() ){
int u = Q.front(); Q.pop(); inQ[u] = false;
for (int i = adj[u]; i != nil; i = nod[i].next){
int v = nod[i].to;
if ( nod[i].c > 0 && d[v] - d[u] > nod[i].w ){
d[v] = d[u] + nod[i].w; fa[v] = u; idx[v] = i;
if ( !inQ[v] ) inQ[v] = true, Q.push(v);
}
}
}
return d[t] != oo;
}
int Argument(int s, int t){
int cf = oo;
for (int i = t; i != s; i = fa[i]) cf = min(cf, nod[idx[i]].c);
for (int i = t; i != s; i = fa[i]){
nod[idx[i]].c -= cf; nod[nod[idx[i]].ba].c += cf;
}
return cf*d[t];
}
int MinCost_Flow(int s, int t, int n){
int ans = 0;
while ( Spfa(s, t, n) ) ans += Argument(s, t);
return ans;
}
void RunTest(int n, int m){
Init(n, m);
for (int i = 1; i <= n; ++i){
int cc;
scanf("%d", &cc);
in[i] -= cc; in[i+n] += cc;
AddEdge(0, i, 0, oo);
AddEdge(i+n, T, 0, cc);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n-i; ++j){
int cc;
scanf("%d", &cc);
if ( cc != -1 ) AddEdge(i+n, i+j, cc, oo);
}
for (int i = 0; i <= 2*n+2; ++i){
if ( in[i] > 0 ) AddEdge(_S, i, 0, in[i]);
else AddEdge(i, _T, 0, -in[i]);
}
AddEdge(S, 0, 0, m);
AddEdge(T, S, 0, oo);
printf("%d\n", MinCost_Flow(_S, _T, 2*n+4));
}
int main()
{
int n, m;
while ( scanf("%d%d", &n, &m) != EOF ){
RunTest(n, m);
}
return 0;
}
[vijos 最大获利]最大流
From:https://www.vijos.org/p/1352
Solution:
和算法导论的思考题差不多,源点连接基站, 汇点连接用户, 若用户i使用基站a,b, 则a,b分别连接i, 容量oo。可以证明, 对任意的有限容量割(S,T), 对用户i属于T, 若基站j与i连接, 则基站j属于T。
反证法: 假设j不属于T, 那么j属于S, 且(j,i)容量为oo, 即S可到i, 与i属于T矛盾。
由此可以得出: 一旦i被选中, 与i关联的基站都被选中。
再计算两个式子: 1.C[S,T]; 2.利润M = sigma(Custom[i]) - sigma(Base[i]), 其中Custom[i]是从T可达的用户(表明被选中), Base[i]是从T可达的基站(表明被选中)
联合1,2即得 利润M = TotalCustom - C[S,T](TotalCustom是所有用户的费用)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <queue>
#include <stack>
#include <functional>
using namespace std;
#define N 500005
#define M 10005
#define oo 0x7fffffff
#define nil (-1)
struct { int c,to,ba,next; } nod[N*4];
int adj[N], d[N], idx[N], fa[N], maxflow, now, S, T;
void Init(int n){
now = 0; S = 0; T = n;
for (int i = 0; i <= n; ++i) adj[i] = nil;
}
int NewNode(int u, int c){
nod[++now].c = c; nod[now].to = u; nod[now].next = nil;
return now;
}
void AddEdge(int u, int v, int c){
int u1 = NewNode(v, c);
int v1 = NewNode(u, 0);
nod[u1].ba = v1; nod[v1].ba = u1;
nod[u1].next = adj[u]; adj[u] = u1;
nod[v1].next = adj[v]; adj[v] = v1;
}
bool Bfs(int n){
for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil;
queue<int> Q;
d[S] = 0; Q.push(S);
while( !Q.empty() ){
int u = Q.front(); Q.pop();
for (int i = adj[u]; i != nil ; i = nod[i].next){
int v = nod[i].to;
if ( nod[i].c > 0 && d[v] == oo ){
d[v] = d[u] + 1;
if ( v == T ) return true;
Q.push(v);
}
}
}
return false;
}
int Dfs(int u){
if ( u != T ){
if ( d[u] != oo ){
for (int i = adj[u]; i != nil; i = nod[i].next){
int v = nod[i].to;
if ( nod[i].c > 0 && d[v] == d[u]+1 ){
fa[v] = u; idx[v] = i;
int r = Dfs(v);
if ( r != nil && r != u ) return r;
}
}
d[u] = oo;
}
} else {
int cf = oo, r = fa[T];
for (int i = T; i != S; i = fa[i]) cf = min(cf, nod[idx[i]].c);
maxflow += cf;
for (int i = T; i != S; i = fa[i]){
nod[idx[i]].c -= cf; nod[nod[idx[i]].ba].c += cf;
if ( !nod[idx[i]].c ) r = fa[i];
}
return r;
}
return nil;
}
int Dinic(int n){
maxflow = 0;
while ( Bfs(n) ) Dfs(S);
return maxflow;
}
void RunTest(int n, int m){
Init(n+m+1);
for (int i = 1; i <= n; ++i){
int cc;
scanf("%d", &cc);
AddEdge(S, i, cc);
}
int sum = 0;
for (int i = 1; i <= m; ++i){
int a,b,cc;
scanf("%d%d%d", &a, &b, &cc);
AddEdge(a, n+i, oo);
AddEdge(b, n+i, oo);
AddEdge(n+i, T, cc);
sum += cc;
}
printf("%d\n", sum - Dinic(n+m+1));
}
int main()
{
int n,m;
while( scanf("%d%d", &n, &m) != EOF ) RunTest(n,m);
return 0;
}
[vijos 炸毁燃料库]最大费用流
From:https://www.vijos.org/p/1499
Solution:
这题建图建到吐血。思路和志愿者招募差不多,差分,流守恒。第i桶燃料记为X[i]。然后,记P[i]为第i个连续区间覆盖下可选燃料的约束。举个例子:
6 5 3
2 3 4 2 1 3
这里只有两个连续区间, 分别是[1,5], [2,6], 然后它们要满足:
P[1] = X[1] + X[2] + X[3] + X[4] + X[5] <= 3
P[2] = X[2] + X[3] + X[4] + X[5] + X[6] <= 3
引入松弛变量Y[I]>=0, 变成如下形式:
P[1] = X[1] + X[2] + X[3] + X[4] + X[5] + Y[1] = 3
P[2] = X[2] + X[3] + X[4] + X[5] + X[6] + Y[2] = 3
引入P[0] = P[3] = 0, 差分:
P[1] - P[0] = X[1] + X[2] + X[3] + X[4] + X[5] + Y[1] = 3
P[2] - P[1] = X[6] - X[1] + Y[2] - Y[1] = 0
P[3] - P[2] = -X[2] - X[3] - X[4] - X[5] - X[6] - Y[2] = -3
我们再把式子变下形:
X[1] + X[2] + X[3] + X[4] + X[5] + Y[1] - 3 = 0
X[6] - X[1] + Y[2] - Y[1] = 0
-X[2] - X[3] - X[4] - X[5] - X[6] - Y[2] + 3 = 0
可以观察, 三个式子和网络流的流守恒很像, 其中X,Y就是流量, 负的表示流出, 正的表示流入, 而负常数表示从源点流出, 正常数表示流入汇点。因此, 尝试用3个等式建立网络流, 每个等式代表一个顶点, 然后从X[i](等式I)到-X[i](等式J)指向边,容量为1, 从Y[i](等式I)到-Y[i](等式J)指向边,容量为oo。再求最大费用流即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
#define N 1005
#define M 105
#define oo 0x6fffffff
#define nil (-1)
struct {int w,c,to,ba,next;} nod[N*N];
bool inQ[N];
vector<int> P[N];
int adj[N], d[N], fa[N], idx[N], v[N], now, S, T;
void Init(int n, int m){
now = 0; S = 0; T = n-m+3;
for (int i = 0; i <= n-m+3; ++i) adj[i] = nil, P[i].clear();
}
int NewNode(int u, int w, int c){
nod[++now].to = u; nod[now].c = c; nod[now].w = w; nod[now].next = nil;
return now;
}
void AddEdge(int u, int v, int w, int c){
int u1 = NewNode(v, w, c);
int v1 = NewNode(u, -w, 0);
nod[u1].ba = v1; nod[v1].ba = u1;
nod[u1].next = adj[u]; adj[u] = u1;
nod[v1].next = adj[v]; adj[v] = v1;
}
bool Spfa(int n){
for (int i = 0; i <= n; ++i) d[i] = -oo, fa[i] = nil, inQ[i] = false;
queue<int> Q;
d[S] = 0; Q.push(S); inQ[S] = true;
while ( !Q.empty() ){
int u = Q.front(); Q.pop(); inQ[u] = false;
for (int i = adj[u]; i != nil; i = nod[i].next){
int v = nod[i].to;
if ( nod[i].c > 0 && d[v] < d[u] + nod[i].w ){
fa[v] = u; idx[v] = i;
d[v] = d[u] + nod[i].w;
if ( !inQ[v] ) inQ[v] = true, Q.push(v);
}
}
}
return d[T] != -oo;
}
int Argument(){
int cf = oo;
for (int i = T; i != S; i = fa[i]) cf = min(cf, nod[idx[i]].c);
for (int i = T; i != S; i = fa[i]){
nod[idx[i]].c -= cf; nod[nod[idx[i]].ba].c += cf;
}
return d[T];
}
int MinCost_Flow(int n){
int cost = 0;
while ( Spfa(n) ) cost += Argument();
return cost;
}
void RunTest(int n, int m, int k){
Init(n, m);
int tot = 1;
for (int i = 1; i < n-m+2; ++i) AddEdge(i, i+1, 0, oo);
for (int i = 1; i <= n; ++i){
scanf("%d", v+i);
if ( i <= m ){
P[tot].push_back(i);
} else {
++tot;
P[tot].push_back(i);
P[tot].push_back(m-i);
}
}
++tot;
for (int i = n-m+1; i <= n; ++i) P[tot].push_back(-i);
for (int i = 1; i <= tot; ++i)
for (int j = 0; j < (int)P[i].size(); ++j){
for (int k1 = i + 1; k1 <= tot; ++k1)
for (int k2 = 0; k2 < (int)P[k1].size(); ++k2){
if ( P[i][j] == - P[k1][k2] ){
AddEdge(i, k1, v[P[i][j]], 1);
}
}
}
AddEdge(S, 1, 0, k); AddEdge(n-m+2, T, 0, k);
printf("%d\n", MinCost_Flow(n-m+3));
}
int main()
{
int n,m,k;
while( scanf("%d%d%d", &n, &m, &k) != EOF ) RunTest(n,m,k);
return 0;
}
[vijos 跳舞]最大流
From:https://www.vijos.org/p/1521
Solution:
当不能解所提题目时, 尽量回忆和之有关的问题, 尝试丢弃或改变条件。如果不考虑k, 那么这个问题就是求能有几次完全匹配。这个问题怎么解决?按最大流方式建图, 只能求一次最大匹配(判断是否是完全匹配)。不难发现,一旦是完全匹配, 那么与源汇连接的边都是0容量, 于是, 我们可以在此基础上, 再给源汇连接的边补上1容量, 继续求最大流, 直到找不出完全匹配的最大流, 就是答案。
现在, 如何求有几次完全匹配的问题我们解决了。在此考虑k, 注意题目提到, 不管男孩女孩最多只和k个不喜欢的跳舞, 转化成图来考虑, 也就是最多能流经k次, 在加上原来互相喜欢的男孩或女孩k'个, 就变成了每个男孩或女孩最多只有k+k'次经过的流。因此, 可以建图为:
1.n个顶点N分别与n个男孩相连, 容量为男孩喜欢的女孩个数+不喜欢的女孩个数。
2.n个女孩分别与n个顶点M相连, 容量为女孩喜欢的男孩个数+不喜欢的男孩个数。
3.n个男孩和n个女孩之间相连。
4.源S连接n个顶点N, n个顶点M连接汇T, 容量为1。
在此图基础上, 用刚才求几次完全匹配的方法跑一遍就是答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <queue> #include <vector> #include <functional> using namespace std; #define N 205 #define M 105 #define oo 0x6fffffff #define nil (-1) int cap[N][N], d[N], fa[N], in[M], out[M], maxflow, S, T; void Init(int n){ S = 0; T = 4*n+1; for (int i = 0; i <= T; ++i){ in[i] = out[i] = 0; for (int j = 0; j <= T; ++j) cap[i][j] = 0; } } bool Bfs(int n){ for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil; queue<int> Q; d[S] = 0; Q.push(S); while ( !Q.empty() ){ int u = Q.front(); Q.pop(); for (int v = 0; v <= n; ++v){ if ( cap[u][v] > 0 && d[v] == oo ){ d[v] = d[u] + 1; if ( v == T ) return true; Q.push(v); } } } return false; } int Dfs(int u, int n){ if ( u != T ){ if ( d[u] != oo ){ for (int v = 0; v <= n; ++v){ if ( cap[u][v] > 0 && d[v] == d[u] + 1 ){ fa[v] = u; int r = Dfs(v, n); if ( r != nil && r != u ) return r; } } d[u] = oo; } } else { int cf = oo, r = fa[T]; for (int i = T; i != S; i = fa[i]) cf = min(cf, cap[fa[i]][i]); maxflow += cf; for (int i = T; i != S; i = fa[i]){ cap[fa[i]][i] -= cf; cap[i][fa[i]] += cf; if ( !cap[fa[i]][i] ) r = fa[i]; } return r; } return nil; } int Dinic(int n){ maxflow = 0; while ( Bfs(n) ) Dfs(S, n); return maxflow; } void RunTest(int n, int k){ Init(n); for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j){ char ch; scanf(" %c", &ch); if ( 'Y' == ch ) ++out[i], ++in[j+n]; cap[i+n][j+2*n] = 1; } for (int i = 1; i <= n; ++i){ cap[S][i] = 1; cap[i+3*n][T] = 1; cap[i][i+n] = out[i] + k; cap[i+2*n][i+3*n] = in[i+n] + k; } int ans = 0; while ( n == Dinic(4*n+1) ){ ans += 1; for (int i = 1; i <= n; ++i) cap[S][i] += 1, cap[i+3*n][T] += 1; } printf("%d\n", ans); } int main() { int n,k; while( scanf("%d%d", &n, &k) != EOF ) RunTest(n,k); return 0; }
[vijos 最小监视代价]最小割
From:https://www.vijos.org/p/1524
Solution:
题目实际就是求从1到m个传输节点不可达需要去掉多少边花费代价最小。直接建图, 源点连接1, 传输节点连接汇点。通过最小割的边就是去掉后的最小代价。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <queue> #include <vector> #include <functional> using namespace std; #define N 105 #define M 705 #define oo 0x6fffffff #define nil (-1) struct {int c,to,next;} nod[M*2]; int adj[N], d[N], fa[N], idx[N], maxflow, now, S, T; void Init(int n){ now = 0; S = 0; T = n+1; for (int i = 0; i <= T; ++i) adj[i] = nil; } int NewNode(int u, int c){ nod[now].to = u; nod[now].c = c; nod[now].next = nil; return now++; } void AddEdge(int u, int v, int c){ int u1 = NewNode(v, c); int v1 = NewNode(u, 0); nod[u1].next = adj[u]; adj[u] = u1; nod[v1].next = adj[v]; adj[v] = v1; } bool Bfs(int n){ for (int i = 0; i <= n; ++i) d[i] = oo, fa[i] = nil; queue<int> Q; d[S] = 0; Q.push(S); while ( !Q.empty() ){ int u = Q.front(); Q.pop(); for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( nod[i].c > 0 && d[v] == oo ){ d[v] = d[u] + 1; if ( v == T ) return true; Q.push(v); } } } return false; } int Dfs(int u){ if ( u != T ){ if ( d[u] != oo ){ for (int i = adj[u]; i != nil; i = nod[i].next){ int v = nod[i].to; if ( nod[i].c > 0 && d[v] == d[u] + 1 ){ fa[v] = u; idx[v] = i; int r = Dfs(v); if ( r != nil && r != u ) return r; } } d[u] = oo; } } else { int cf = oo, r = fa[T]; for (int i = T; i != S; i = fa[i]) cf = min(cf, nod[idx[i]].c); maxflow += cf; for (int i = T; i != S; i = fa[i]){ nod[idx[i]].c -= cf; nod[idx[i]^1].c += cf; if ( !nod[idx[i]].c ) r = fa[i]; } return r; } return nil; } int Dinic(int n){ maxflow = 0; while ( Bfs(n) ) Dfs(S); return maxflow; } void RunTest(int n, int m){ Init(n); for (int i = 0; i < m; ++i){ int a, b, cc; scanf("%d%d%d", &a, &b, &cc); AddEdge(a, b, cc); AddEdge(b, a, cc); } int k; scanf("%d", &k); for (int i = 0; i < k; ++i){ int a; scanf("%d", &a); AddEdge(a, T, oo); } AddEdge(S, 1, oo); printf("%d\n", Dinic(n+1)); } int main() { int n,m; while( scanf("%d%d", &n, &m) != EOF ) RunTest(n,m); return 0; }