图论专题I

 

[pku2396 Budget]带源汇的上下界最大流

From:http://poj.org/problem?id=2396

 

#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;
}
View Code

 

[zoj2314 Reactor Cooling]无源汇的上下界可行流

From:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1314

#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;
}
View Code

 

[noi 志愿者招募]带源汇的上下界费用流

From:http://www.lydsy.com/JudgeOnline/problem.php?id=1061

Solution:

差分, 流守恒性, 容量限制(https://www.byvoid.com/blog/noi-2008-employee)

#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;
}
View Code

 

[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个城市恰好经过。

#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;
}
View Code

 

[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是所有用户的费用)

#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;
}
View Code

 

[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。再求最大费用流即可。

#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;
}
View Code

 

[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。

在此图基础上, 用刚才求几次完全匹配的方法跑一遍就是答案。

 

#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;
}
View Code

 

[vijos 最小监视代价]最小割

From:https://www.vijos.org/p/1524

Solution:

题目实际就是求从1到m个传输节点不可达需要去掉多少边花费代价最小。直接建图, 源点连接1, 传输节点连接汇点。通过最小割的边就是去掉后的最小代价。

 

#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;
}
View Code

 

 

 

 

 

posted on 2013-12-13 13:35  leezyli  阅读(256)  评论(0编辑  收藏  举报

导航