网络流专题

 

POJ 1149 PIGS 

链接

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 10005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;
void add_edge(int u,int v,int w) {
//    cout << u << " " << v << " " << w << "\n";
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) 
    ans += dfs(S, INF);
    return ans;
}

vector<int> vec[N];
int w[N], cnt[N];

int main() { 
//freopen("1.txt", "r", stdin);
    int m = read(), n = read();
    for (int i = 1; i <= m; ++i) w[i] = read();
    for (int i = 1; i <= n; ++i) {
        int t = read();
        while (t--) {
            int u = read();
            vec[u].push_back(i);
        }
        cnt[i] = read();
    }
    S = 0; T = n + 1;
    for (int i = 1; i <= n; ++i) add_edge(i, T, cnt[i]);
    for (int i = 1; i <= m; ++i) {
        if (vec[i].size() == 0) continue;
        add_edge(S, vec[i][0], w[i]);
        for (int j = 1; j < vec[i].size(); ++j) {
            add_edge(vec[i][j - 1], vec[i][j], INF);
        }
    }
    cout << dinic();
    return 0;
}
View Code

 

POJ 2699 The Maximum Number of Strong Kings

链接

分析:枚举strong king的个数,然后网络流判断是否可行。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;

void add_edge(int u,int v,int w) {
//    cout << u << " " << v << " " << w << "\n";
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

int n, tot, a[N], id[N][N];
bool solve(int k) {
    En = 1; memset(head, 0, sizeof(head));
    S = 0; T = n + tot + 1;
    for (int i = 1; i <= n; ++i) add_edge(S, i, a[i]);
    for (int i = n + 1; i <= n + tot; ++i) add_edge(i, T, 1);
    for (int i = 1; i <= n; ++i) {
        for (int j = i + 1; j <= n; ++j) {
            if (i >= n - k + 1 && a[i] < a[j]) add_edge(i, id[i][j] + n, 1);
            else add_edge(i, id[i][j] + n, 1), add_edge(j, id[i][j] + n, 1);
        }
    }
    return dinic() == tot;
}
void solve() {
    n = tot = 0;
    string s;
    getline(cin, s);
    int now = 0;
//    for (int i = 0; i < (int)s.size(); ++i) {
//        if (s[i] == ' ') a[++n] = now, now = 0;
//        else now = now * 10 + s[i] - '0';
//    } 
//    a[++n] = now;
    for (int i = 0; i < (int)s.size(); ++i) { // 注意此处的读入!!!不要向上面一样读入!!! 
        if (isdigit(s[i])) {
            now = 0;
            while (i < s.size() && isdigit(s[i])) {
                now = now * 10 + (s[i] - '0');
                i ++;
            }
            i --;
            a[++n] = now;
        }
    }
    for (int i = 1; i <= n; ++i) 
        for (int j = i + 1; j <= n; ++j) id[i][j] = id[j][i] = ++tot;
    for (int i = n; i >= 1; --i) 
        if (solve(i)) { printf("%d\n", i); return ; }
    puts("0");
}
int main() {
    int T = read();
    for (; T--; ) solve();
    return 0;
}
View Code

 

ZOJ 2760 How Many Shortest Path 

链接

分析:求出那些边可以存在于最短路上,然后跑网络流。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[30005];
int head[N], cur[N], dis[N], q[N], En = 1, S, T, n;

void add_edge(int u,int v,int w) {
//    printf("%d %d %d\n", u, v, w);
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 1; i <= n; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}
int g[N][N], t[N][N]; 
void solve() {
    En = 1; memset(head, 0, sizeof(head));
    if (S == T) { puts("inf"); return ; }
    for (int k = 1; k <= n; ++k) 
        for (int i = 1;i  <= n; ++i) 
            for (int j = 1; j <= n; ++j) 
                if (g[i][k] != -1 && g[k][j] != -1 && (g[i][k] + g[k][j] < g[i][j] || g[i][j] == -1)) 
                    g[i][j] = g[i][k] + g[k][j];
    if (g[S][T] == -1) { puts("0"); return ; }
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) 
            if (i != j && g[S][T] == g[S][i] + t[i][j] + g[j][T] && g[S][i] != -1 && g[j][T] != -1) 
            add_edge(i, j, 1);
    printf("%d\n", dinic());    
}
int main() {
//    freopen("1.txt", "r", stdin);
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; ++i) 
            for (int j = 1; j <= n; ++j) t[i][j] = g[i][j] = read();
        for (int i = 1; i <= n; ++i) t[i][i] = g[i][i] = 0;
        S = read() + 1, T = read() + 1;
        solve();
    }
    return 0;
}
View Code

 

WOJ 124 Football match

链接

题意:

有 N 支球队,互相之间已经进行了一些比赛,还剩下M场没有比。现在给出各 支球队目前的总分以及还剩下哪 M 场没有比,问能否合理安排这 M 场比赛的结 果,使得第 N 支球队最后的总分大于其他任何一支球队的总分。已知每场比赛 胜者得 2 分,败者 0 分,平局则各得 1 分。(1 <= N <= 100, 0 <= M <= 1000)

分析:首先让所有n参见的比赛,让n赢,然后剩下的建图跑最大流,判断是否可行。S向每个比赛连,流量为2,每个比赛向两个球队连,流量为2,每个球队向T连,流量为w[n]-w[i]-1,表示最大的得分。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 10005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;

void add_edge(int u,int v,int w) {
    // printf("%d %d %d\n", u, v, w);
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

int n, m, a[N], b[N], w[N];
void solve() {
    En = 1; memset(head, 0, sizeof(head));
    int tot = 0, now = 0; S = 0;
    for (int i = 1; i <= m; ++i) {
        if (a[i] == n || b[i] == n) w[n] += 2, a[i] = b[i] = 0;
        else {
            now ++; add_edge(S, now, 2);
        }
    }
    // cout << "match" << now << "\n";
    for (int i = 1; i <= n; ++i) 
        if (w[i] > w[n]) { puts("NO"); return ; }
    tot = now; now = 0;
    for (int i = 1; i <= m; ++i) {
        if (a[i] && b[i]) {
            now ++;
            add_edge(now, a[i] + tot, 2);
            add_edge(now, b[i] + tot, 2);
        }
    }
    T = tot + n + 1;
    for (int i = 1; i < n; ++i) 
        add_edge(i + tot, T, w[n] - w[i] - 1);
    puts(dinic() == tot * 2 ? "YES" : "NO");
}
int main() {
    // freopen("1.txt", "r", stdin);
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 1; i <= n; ++i) w[i] = read();
        for (int i = 1; i <= m; ++i) a[i] = read(), b[i] = read();
        solve();
    }
    return 0;
}
View Code

 

SGU 438. The Glorious Karlutka River =)

链接

题意:一条宽为W的河,河中有N块石头,每块石头的坐标(Xi, Yi)和每一时刻最大承受人数Ci。有M个游客,每次最远跳D米,每跳一次1秒。问能否所有人穿越这条河流,如果能,最少需要多长时间。 N,M<=50

分析:动态流问题,首先可以通过判断是否联通来得知能否过河(或者利用最差清空的时间是n+m来判断)。然后按时间拆点,建图跑网络流。每个石头拆成两个点,然后对于一条边u,v,从上一秒的u到下一秒的v连一条inf的边,每秒源点也要连出边。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
#define pa pair<int,int>
#define mp make_pair
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 100005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T, Index;
void add_edge(int u,int v,int w) {
//    printf("%d %d %d\n", u, v, w);
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= Index; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

int n, m, D, L, x[N], y[N], w[N], fa[N], chu[N], ru[N];
vector< pa > g;
vector<int> p;

int sqr(int x) { return x * x; }
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
bool pd() {
    for (int i = 1; i <= n + 2; ++i) fa[i] = i;
    for (int i = 1; i <= n; ++i) {
        if (!w[i]) continue;
        if (y[i] <= D) {
            int r1 = find(n + 1), r2 = find(i);
            if (r1 != r2) fa[r2] = r1;
            g.push_back(mp(0, i));
            p.push_back(i);
        } if (y[i] >= L - D) {
            int r1 = find(n + 2), r2 = find(i);
            if (r1 != r2) fa[r2] = r1;
            p.push_back(i);
            g.push_back(mp(i, n + 1));
        }
    }
    for (int i = 1; i <= n; ++i) 
        for (int j = i + 1; j <= n; ++j) {
            if (!w[i] || !w[j] || i == j) continue;
            if (sqr(x[i] - x[j]) + sqr(y[i] - y[j]) > D * D) continue;
            int r1 = find(i), r2 = find(j);
            if (r1 != r2) fa[r2] = r1;
            p.push_back(i), p.push_back(j);
            g.push_back(mp(i, j));
        }
    p.push_back(0), p.push_back(n + 1);
    sort(p.begin(), p.end());
    p.erase(unique(p.begin(), p.end()), p.end());
    return find(n + 1) != find(n + 2);
}
int main() {
    n = read(), m = read(), D = read(), L = read();
    if (D >= L) { puts("1"); return 0; } // D >= L not D > L !!!!! 
    for (int i = 1; i <= n; ++i) {
        x[i] = read(), y[i] = read(), w[i] = read();
    }
    if (pd()) { puts("IMPOSSIBLE"); return 0; }
    
    S = 0, T = n + n + 1; Index = T;
    for (int i = 1; i <= n; ++i) {
        ru[i] = i, chu[i] = i + n;
        if (y[i] <= D) 
            add_edge(S, i, INF), add_edge(i, i + n, w[i]);
    }
    chu[S] = ru[S] = S, chu[n + 1] = ru[n + 1] = T; w[T] = INF;int flow = 0;
    for (int Ti = 1; Ti <= n + m; ++Ti) {
        flow += dinic(); 
        if (flow >= m) { cout << Ti; return 0; }
        for (int i = 1; i < (int)p.size() - 1; ++i) ru[p[i]] = ++Index; 
        for (int i = 0; i < (int)g.size(); ++i) {
            int u = g[i].first, v = g[i].second;
             if (p[v] != S && chu[u] != T) 
                add_edge(chu[u], ru[v], INF);
            if (p[u] != S && chu[v] != T) 
                add_edge(chu[v], ru[u], INF);
        }
        for (int i = 1; i < (int)p.size() - 1; ++i) {
            chu[p[i]] = ++Index;
            add_edge(ru[p[i]], chu[p[i]], w[p[i]]);
        }
    }
    return 0;
}
View Code

 

SPOJ NETADMIN - Smart Network Administrator

链接

分析: 二分一个mid,然后给每条边设置容量为mid,从源点向每个想要链接internet的用户连一条容量为1的边,从1向汇点连一条容量为k的边。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 100005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;
void add_edge(int u,int v,int w) {
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

#define pa pair<int,int>
vector< pa > g;
int n, m, k;
int h[N];
bool check(int c) {
    En = 1; memset(head, 0, sizeof(head));
    S = 0, T = n + 1;
    for (int i = 1; i <= k; ++i) add_edge(S, h[i], 1);
    add_edge(1, T, k);
    for (int i = 0; i < (int)g.size(); ++i) {
        add_edge(g[i].first, g[i].second, c);
        add_edge(g[i].second, g[i].first, c);
    }
    return dinic() == k;
}
void solve() {
    g.clear();
    n = read(), m = read(), k = read();
    for (int i = 1; i <= k; ++i) h[i] = read();
    for (int i = 1; i <= m; ++i) g.push_back(pa(read(), read()));
    int L = 1, R = k, ans = 0;
    while (L <= R) {
        int mid = (L + R) >> 1;
        if (check(mid)) ans = mid, R = mid - 1;
        else L = mid + 1;
    }
    cout << ans << "\n";
}
int main() {
    freopen("1.txt", "r", stdin);
    for (int T = read(); T --; ) solve();
    return 0;
}
View Code

 

SPOJ IM - Intergalactic Map

链接

题意:无向图中,能否从1走到2,再从2走到3,每个点只经过一次。

分析:源点向2连,容量为2,1和3向汇点连,容量为1,建图跑网络流,判断容量是否为2。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 200005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;
void add_edge(int u,int v,int w) {
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

void solve() {
    En = 1; memset(head, 0, sizeof(head));
    int n = read(), m = read();
    S = 0, T = n + n + 1;
    for (int i = 1; i <= n; ++i) add_edge(i, i + n, 1);
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read();
        if (u < 1 || v < 1 || u > n || v > n) continue; // 注意!!! 
        add_edge(u + n, v, 1);
        add_edge(v + n, u, 1);
    }
    add_edge(S, 2 + n, 2);
    add_edge(1 + n, T, 1);
    add_edge(3 + n, T, 1);
    puts(dinic() == 2 ? "YES" : "NO");
}
int main() {
    for (int T = read(); T --; ) solve(); 
    return 0;
}
View Code

 

 

POJ 1637 Sightseeing tour

链接

题意:混合图欧拉回路的判定。

分析:首先给无向图随便定向,然后判断每个点的初度-入度有没有奇数,如有有,无解。否则S向出>入的点连,容量为(出-入)/2,入>出的点向T连,容量为(入-出)/2,原图中的无向边保留,有向边删掉,跑最大流,有解的条件是满流。

理解:每个的点度数之差如果是奇数,无论怎么改变无向边的方向,这个点的度数之差始终是奇数,所以无解;否则可以尝试着修改一些无向边的方向,使得每个点的出度等于入度。那么在网络中,如果一个点x的出度>入度,那么说明这个点需要改变一些出边变成入边,来满足条件,改变的条数是c=(出-入)/2;如果点y的入度>出度,那么就是改变入边,条数d=(入-出)/2。S向x连一条容量为c的边,y向T连一条容量为d的边。网络流中的一条增广路径经过的边,就是需要反转的边,如果满流,说明存在解。详细的解释证明

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 3005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;
void add_edge(int u,int v,int w) {
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

int x[N], y[N], d[N], ru[N], chu[N];
void init() {
    En = 1; 
    memset(head, 0, sizeof(head));
    memset(ru, 0, sizeof(ru));
    memset(chu, 0, sizeof(chu));
}
void solve() {
    init();
    int n = read(), m = read(), sum = 0;
    for (int i = 1; i <= m; ++i) {
        x[i] = read(), y[i] = read(), d[i] = read();
        ru[y[i]] ++, chu[x[i]] ++;
    }
    for (int i = 1; i <= n; ++i) {
        if (abs(ru[i] - chu[i]) & 1) { puts("impossible"); return ; }
    }
    S = 0, T = n + 1;
    for (int i = 1; i <= m; ++i) {
        if (!d[i]) add_edge(x[i], y[i], 1);
    }
    for (int i = 1; i <= n; ++i) {
        if (chu[i] == ru[i]) continue;
        if (ru[i] > chu[i]) add_edge(i, T, (ru[i] - chu[i]) / 2);
        if (chu[i] > ru[i]) add_edge(S, i, (chu[i] - ru[i]) / 2), sum += (chu[i] - ru[i]) / 2;
        
    }
    puts(dinic() == sum ? "possible" : "impossible");
}
int main() {
    for (int T = read(); T -- ; ) solve();
    return 0;
}
View Code

 

2756: [SCOI2012]奇怪的游戏

链接

分析:黑白染色,讨论,网络流判断。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2005;
const LL INF = 1e16;
struct Edge{
    int to, nxt;LL cap;
}e[50005];
int head[N], cur[N], dis[N], q[N], En = 1, S, T, n, m;
void add_edge(int u,int v,LL w) {
//    printf("%d %d %I64d\n", u, v, w);
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
LL dfs(int u,LL flow) {
    if (u == T) return flow;
    LL used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
LL dinic() {
    LL ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}
LL a[50][50];
int id[50][50];
bool check(LL k) {
    LL t = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            if ((i + j) & 1) add_edge(id[i][j], T, k - a[i][j]);
            else {
                add_edge(S, id[i][j], k - a[i][j]); t += k - a[i][j];
                if (i > 1) add_edge(id[i][j], id[i - 1][j], INF);
                if (i < n) add_edge(id[i][j], id[i + 1][j], INF);
                if (j > 1) add_edge(id[i][j], id[i][j - 1], INF);
                if (j < m) add_edge(id[i][j], id[i][j + 1], INF);
            }
        }
    t = (dinic() == t);
    En = 1;
    for (int i = 0; i <= T; ++i) head[i] = 0;
    return t;
}
void solve() {
    n = read(), m = read(); 
    LL Mx = 0;
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) a[i][j] = read(), Mx = max(Mx, a[i][j]);
    LL sum0 = 0, sum1 = 0, cnt0 = 0, cnt1 = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if ((i + j) & 1) sum1 += a[i][j], cnt1 ++;
            else sum0 += a[i][j], cnt0 ++;
        }
    }
    if (cnt0 == cnt1 && sum0 != sum1) { puts("-1"); return ; }
    S = 0, T = n * m + 1;
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) id[i][j] = (i - 1) * m + j;
    if (cnt0 == cnt1) {
        LL L = Mx, R = INF, ans = 0;
        while (L <= R) {
            LL mid = (L + R) >> 1;
            if (check(mid)) ans = mid, R = mid - 1;
            else L = mid + 1;
        }
        printf("%lld\n", (ans * cnt0 - sum0));
    }
    else {
        LL k = (sum0 - sum1) / (cnt0 - cnt1);
        if (k >= Mx && check(k)) printf("%lld\n", (k * cnt0 - sum0));
        else puts("-1");
    }
}
int main() {
    for (int T = read(); T --; ) solve();
    return 0;
}
View Code

 

UVA 11082 Matrix Decompressing

链接

分析:经典问题,行列拆成一个点,S->行点,容量为行的和,列点->T,容量为列的和,中间两两连边,注意一下,原题中不能有0,权值范围为[1,20],因为所有边的容量一样,可以同时减去1(注意S->行,列->T的边也要减),然后求出后统一+1。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2005, INF = 1e9;
struct Edge{
    int to, nxt, cap;
}e[N << 1];
int head[N], cur[N], dis[N], q[N], En = 1, S, T;
void add_edge(int u,int v,int w) {
    ++En; e[En].to = v; e[En].nxt = head[u]; e[En].cap = w; head[u] = En;
    ++En; e[En].to = u; e[En].nxt = head[v]; e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0;
    q[++R] = S; dis[S] = 0; 
    while (L <= R) {
        int u = q[L ++];
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1; 
                q[++R] = v;
                if (v == T) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, t;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            t = dfs(v, min(e[i].cap, flow - used));
            if (t > 0) {
                used += t, e[i].cap -= t, e[i ^ 1].cap += t;
                if (used == flow) break;
            }
        }
    }
    if (used != flow) dis[u] = -1;
    return used;
    
}
int dinic() {
    int ans = 0;
    while (bfs()) ans += dfs(S, INF);
    return ans;
}

int a[N], b[N], id[N][N];
void solve() {
    int n = read(), m = read();
    S = 0, T = n + m + 1;
    for (int i = 1; i <= n; ++i) {
        a[i] = read();
        add_edge(S, i, a[i] - a[i - 1] - m);
    }
    for (int i = 1; i <= m; ++i) {
        b[i] = read();
        add_edge(i + n, T, b[i] - b[i - 1] - n);
    }
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) add_edge(i, j + n, 19), id[i][j] = En;
    dinic();
    for (int i = 1; i <= n; ++i, puts("")) 
        for (int j = 1; j <= m; ++j) 
            printf("%d ", 1 + e[id[i][j]].cap);
    puts("");
    En = 1;
    for (int i = 0; i <= T; ++i) head[i] = 0;
}
int main() {
    for (int T = read(), i = 1; i <= T; ++i) {
        printf("Matrix %d\n", i);
        solve();
    }
    return 0;
}
View Code

 

1927: [Sdoi2010]星际竞速

链接

分析:首先注意到这是一个DAG,每个点要保证只经过一次,可以从任意点出发。类似一个哈密顿回路(如果把瞬移也加入到图中的话)。考虑一个点是从哪里来的,可以从任意点直接瞬移到这里,可以从连向这个点的点走到这里。

1、那么由于从任意点瞬移到这个点的花费是一定的,所以可以直接从S->i+n(这些点设为i+n)连边,花费为瞬移的时间,容量为1。

2、从连向这个点的位置走过来,花费为路径的权值,由于这是一个类似哈密顿路径的东西,每个点也只能出去一次,所以再建一列点(i),表示从每个点出去,S->i,容量为1(表示这个点只能出去一次),花费为0;对于一条边u->v,连边u->v+n,容量为1,花费为边权。

3、u+n->T连边,容量为1,花费为0。u+n表示已经到过u这个点了,容量为1,表示只能由一个点到这个点(哈密顿路径中,要求出度入度都为1)。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
#define pa pair<int,int>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2005, INF = 1e9;
struct Edge{ int fr, to, nxt, cap, cost; } e[50005];
int head[N], dis[N], pre[N], En = 1, S, T, q[100005]; 
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}
bool spfa() {
    for (int i=1; i<=T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0;
    q[++R] = S, dis[S] = 0, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i=head[u]; i; i=e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != INF;
}
void mcf() {
    LL Flow = 0, Cost = 0;
    while (spfa()) {
        int now = INF;
        for (int i = T; i != S; i = e[pre[i]].fr) 
            now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].fr) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Cost += 1ll * now * dis[T];
        Flow += now;
    }
    cout << Cost;
}
int main() {
    int n = read(), m = read();
    S = 0, T = n + n + 1;
    for (int i = 1; i <= n; ++i) {
        int x = read();
        add_edge(S, i + n, 1, x);
        add_edge(S, i, 1, 0);
        add_edge(i + n, T, 1, 0);
    }
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), w = read();
        if (u > v) swap(u, v);
        add_edge(u, v + n, 1, w);
    }
    mcf();
    return 0;
}
View Code

 

2324: [ZJOI2011]营救皮卡丘

链接

分析:费用流,建模。首先转化为有向图无环图(DAG)的最小权路径覆盖问题,预处理出g[i][j],表示i->j只经过编号小于j或者i的点,最短路。然后就是一张n*n的竞赛图了(有向),选小于等于k条路径,覆盖所有点,花费最小。S->0,容量为k,花费为0,S-1~n,容量为1,花费为0;对于u->v,连接u->v+n,容量为1,花费为g[i][j];连接i+n->T,容量为1,花费为0。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 505, INF = 1e9;
int g[N][N];
struct Edge{
    int fr, to, nxt, cap, cost; 
}e[500005];
int head[N], dis[N], pre[N], q[100005], En = 1, S, T;
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;    
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}

bool spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0; 
    dis[S] = 0, q[++R] = S, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != INF;
}
int mcf() {
    int Flow = 0, Cost = 0;
    while (spfa()) {
        int now = INF;
        for (int i = T; i != S; i = e[pre[i]].fr) now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].fr) e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Flow += now;
        Cost += now * dis[T];
    }
    return Cost;
}
int main() {
    int n = read(), m = read(), k = read();
    memset(g, 0x3f, sizeof(g));
    for (int i = 1; i <= n; ++i) g[i][i] = 0;
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), w = read();
        g[u][v] = g[v][u] = min(g[u][v], w);
    }
    for (int k = 0; k <= n; ++k) 
        for (int i = 0; i <= n; ++i)
            for (int j = 0; j <= n; ++j) 
                if (k <= i || k <= j) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    S = n + n + 1, T = S + 1;
    add_edge(S, 0, k, 0);
    for (int i = 1; i <= n; ++i) 
        add_edge(S, i, 1, 0), add_edge(i + n, T, 1, 0);
    for (int i = 0; i <= n; ++i) 
        for (int j = i + 1; j <= n; ++j) 
            add_edge(i, j + n, 1, g[i][j]);
    printf("%d\n", mcf());
    return 0;
}
View Code

 

CF 277 E. Binary Tree on Plane

链接

分析:每个点拆成两个点,一个表示作为父节点(i),另一个表示作为子节点(i+n)。然后S向i连边,容量为2(表示只能选两个子节点),花费为0;i+n向T连边容量为1,花费为0;如果i可以作为j的父节点,那么i向j+n连边,容量为1,花费为i与j之间的距离。

如果存在解,那么跑完最小费用最大流后,流量要求大于等于n-1。 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 500005, INF = 1e9;
const double DINF = 1e18;
struct Edge{
    int fr, to, nxt, cap;double cost; 
}e[N << 1];
int head[N], pre[N], q[N], En = 1, S, T, n; double dis[N];
bool vis[N];

inline void add_edge(int u,int v,int f,double w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}
bool spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = DINF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0; 
    dis[S] = 0, q[++R] = S, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != DINF;
}
void mcf() {
    int Flow = 0; double Cost = 0;
    while (spfa()) {
        int now = INF;
        for (int i = T; i != S; i = e[pre[i]].fr) 
            now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].fr) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Flow += now;
        Cost += 1.0 * now * dis[T];
    }
    if (Flow >= n - 1) printf("%.10lf", Cost);
    else puts("-1");
}

struct Node{ int x, y; } A[N];
int sqr(int x) { return x * x; }
double dist(const Node &A, const Node &B) {
    return sqrt(1.0 * (sqr(A.x - B.x) + sqr(A.y - B.y)));
}
int main() {
    n = read();
    for (int i = 1; i <= n; ++i) A[i].x = read(), A[i].y = read();
    S = 0, T = n + n + 1;
    for (int i = 1; i <= n; ++i) add_edge(S, i, 2, 0), add_edge(i + n, T, 1, 0);
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) 
            if (A[j].y > A[i].y) add_edge(j, i + n, 1, dist(A[i], A[j]));
    mcf();
    return 0;
}
View Code

 

2879: [Noi2012]美食节

链接

分析:费用提前计算。考虑第i分菜被第j个厨师做,这是他的第k份,一共做了t份,那么这份菜的花费就是(t-k+1)*T[i][j]。由于t是未知的,这样建图还是不太行,换种方法:考虑这个厨师倒数第k份菜是那个,那么它的花费就是k*T[i][j]。所以可以这样建图:每个厨师拆成sum_p个点,S向每个点连边,容量为1费用为0;每个点向每份菜连边,容量为1费用为T[i][j];每份菜向T连边,容量为p[i]费用为0。这样的话,边太多了,考虑优化,动态加边,每次增广会找到一条从S->厨师(倒数第i时刻的第j个厨师)->菜->T,因为时刻越靠前花费越多,所以一定会先找大的时刻,所以可以一开始加出所有的倒数第一个时刻的厨师,然后每次增广了一条路,找到这个厨师,把这个厨师的下一个时刻建出。

// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 500005, INF = 1e9;
struct Edge{
    int fr, to, nxt, cap, cost; 
}e[N << 1];
int head[N], dis[N], pre[N], q[N], cnt[N], id[N], p[N], t[100][200], En = 1, S, T, n, m, Sum, Index;
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}
bool spfa() {
    for (int i = 0; i <= Index; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0; 
    dis[S] = 0, q[++R] = S, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true; // vis[v]=true!!! 
            }
        }
    }
    return dis[T] != INF;
}
void mcf() {
    int Flow = 0, Cost = 0;
    while (spfa()) {
        int now = INF, x;
        for (int i = T; i != S; i = e[pre[i]].fr) {
            now = min(now, e[pre[i]].cap);
            if (e[pre[i]].fr == S) x = e[pre[i]].to;
        }
        for (int i = T; i != S; i = e[pre[i]].fr) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Flow += now;
        Cost += now * dis[T];
        if (Flow == Sum) { cout << Cost; return ; }
        x = id[x]; cnt[x] ++;
        if (cnt[x] <= Sum) {
            ++Index; id[Index] = x;
            add_edge(S, Index, 1, 0);
            for (int j = 1; j <= n; ++j) add_edge(Index, j, 1, t[j][x] * cnt[x]);
        }
    }
}
int main() {
    n = read(), m = read(); 
    for (int i = 1; i <= n; ++i) p[i] = read(), Sum += p[i]; 
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) t[i][j] = read();
    S = 0, T = m + n + 1;
    for (int i = 1; i <= n; ++i) add_edge(i, T, p[i], 0);
    for (int i = 1; i <= m; ++i) add_edge(S, i + n, 1, 0), id[i + n] = i, cnt[i] = 1;
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j) add_edge(i + n, j, 1, t[j][i]);
    Index = T;
    mcf();
    return 0;
}
View Code

 

HDU 3667 Transportation

链接

分析:每条边差分后拆成5条,分别是a,3a,5a,7a,9a。然后最小费用最大流。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 105, INF = 1e9;
struct Edge{
    int fr, to, nxt, cap, cost; 
}e[60005];
int head[N], dis[N], pre[N], q[100005], En = 1, S, T, n, m, k;
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}
bool spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0; 
    dis[S] = 0, q[++R] = S, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != INF;
}
void mcf() {
    int Flow = 0, Cost = 0;
    while (spfa()) {
        int now = INF;
        for (int i = T; i != S; i = e[pre[i]].fr) 
            now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].fr) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Flow += now;
        Cost += now * dis[T];
    }
    if (Flow != k) puts("-1");
    else printf("%d\n", Cost);
}
void solve(){
    S = 0, T = n;
    add_edge(S, 1, k, 0);
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), a = read(), c = read();
        for (int j = 1; j <= c; ++j) add_edge(u, v, 1, (j * 2 - 1) * a);
    }
    mcf();
    En = 1;
    for (int i = 0; i <= T; ++i) head[i] = 0;
}
int main() {
    while (~scanf("%d%d%d", &n, &m, &k)) solve();
    return 0;
}
View Code

 

UVA 12092 Paint the Roads 

链接

分析:每个点存在于k个环中,转化为每个点的出度,入度都等于k。然后拆成两列点,S->i,容量为k花费为0;i->j+n,容量为1花费为边的长度;i+n->T,容量为k花费为0。最小费用最大流。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 105, INF = 1e9; 
struct Edge{
    int fr, to, nxt, cap, cost; 
}e[10005];
int head[N], dis[N], pre[N], q[10005], En = 1, S, T, n, m, k, Sum;
bool vis[N];

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].fr = u, e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
    ++En; e[En].fr = v, e[En].to = u, e[En].nxt = head[v], e[En].cap = 0, e[En].cost = -w; head[v] = En;
}
bool spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, pre[i] = 0;
    int L = 1, R = 0; 
    dis[S] = 0, q[++R] = S, vis[S] = true, pre[S] = 0;
    while (L <= R) {
        int u = q[L ++]; vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = i;
                if (!vis[v]) q[++R] = v, vis[v] = true;
            }
        }
    }
    return dis[T] != INF;
}
void mcf() {
    int Flow = 0, Cost = 0;
    while (spfa()) {
        int now = INF;
        for (int i = T; i != S; i = e[pre[i]].fr) 
            now = min(now, e[pre[i]].cap);
        for (int i = T; i != S; i = e[pre[i]].fr) 
            e[pre[i]].cap -= now, e[pre[i] ^ 1].cap += now;
        Flow += now;
        Cost += now * dis[T];
    }
    if (Flow == Sum) printf("%d\n", Cost);
    else puts("-1");
}
void solve() {
    n = read(), m = read(), k = read(); 
    S = 0, T = n + n + 1; Sum = n * k;
    for (int i = 1; i <= n; ++i) 
        add_edge(S, i, k, 0), add_edge(i + n, T, k, 0);
    for (int i = 1; i <= m; ++i) {
        int u = read() + 1, v = read() + 1, d = read();
        add_edge(u, v + n, 1, d);
    }
    mcf();
    En = 1;
    for (int i = 0; i <= T; ++i) head[i] = 0;
}
int main() {
    for (int T = read(); T--; ) solve();
    return 0;
}
View Code

 

POJ 2175 Evacuation Plan

链接

题意:n个建筑物,每个建筑物里有ai个人,m个避难所,每个避难所可以容纳bi个人,现在给出一个分配方案(g[i][j]表示第i个建筑物去第j个避难所的人数),问是否总的距离是否是最小的,如果不是,输出任意一组比当前优秀的解。

分析:消圈定理:所谓消圈定理,就是在某个流f中,如果其对应的残余网络没有负圈(剩余流量为0的边视为不存在),那它一定就是当前流量下的最小费用流。反之亦然。即「f是最小费用流等价于其残余网络中没有负圈」。by sengxian。那么在这道题中,建出残余网络,找一下负圈,如果存在,那么是说明存在更优的方案,然后在圈上增广一下,得到更优的方案。否则,不存在更优的方案。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cctype>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 205, INF = 1e9;
struct Edge{
    int to, nxt, cap, cost; 
}e[100005];
struct Node{ int x, y, z; } A[N], B[N];
int g[N][N], cnt[N], head[N], dis[N], pre[N], deg[N], En = 1, S, T, n, m;
bool vis[N];
queue<int> q;

inline void add_edge(int u,int v,int f,int w) {
    ++En; e[En].to = v, e[En].nxt = head[u], e[En].cap = f, e[En].cost = w; head[u] = En;
}
int spfa() {
    for (int i = 0; i <= T; ++i) dis[i] = INF, vis[i] = false, deg[i] = pre[i] = 0;
    q.push(S); dis[S] = 0, vis[S] = true, deg[S] ++;
    while (!q.empty()) {
        int u = q.front(); q.pop(), vis[u] = false;
        for (int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if (dis[v] > dis[u] + e[i].cost && e[i].cap > 0) {
                dis[v] = dis[u] + e[i].cost;
                pre[v] = u;
                if (!vis[v]) {
                    q.push(v), vis[v] = true;
                    if ((++deg[v]) > T) return v;
                }
            }
        }
    }
    return -1;
}
void solve(int x) {
    for (int i = 0; i <= T; ++i) vis[i] = 0;
    while (!vis[x]) vis[x] = 1, x = pre[x]; // 这里的中间是逗号,写成了分号。。 
    int z = x, y;
    do {
        y = pre[x];
        if (y <= n && x > n) g[y][x - n] ++;
        if (y > n && x <= n) g[x][y - n] --;
        x = pre[x];
    } while (x != z);
}
int main() {
    n = read(), m = read();
    S = 0, T = n + m + 1;
    for (int i = 1; i <= n; ++i) 
        A[i].x = read(), A[i].y = read(), A[i].z = read();
    for (int i = 1; i <= m; ++i) 
        B[i].x = read(), B[i].y = read(), B[i].z = read();
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) {
            g[i][j] = read();
            cnt[j] += g[i][j];
            int c = abs(A[i].x - B[j].x) + abs(A[i].y - B[j].y) + 1;
            add_edge(i, j + n, INF - g[i][j], c);
            add_edge(j + n, i, g[i][j], -c);
        }
    for (int i = 1; i <= n; ++i) {
        add_edge(S, i, A[i].z, 0);
        add_edge(i, S, 0, 0);
    }
    for (int i = 1; i <= m; ++i) {
        add_edge(i + n, T, B[i].z - cnt[i], 0);
        add_edge(T, i + n, cnt[i], 0);
    }
    int st = spfa();
    if (st == -1) { puts("OPTIMAL"); return 0; }
    solve(st);
    puts("SUBOPTIMAL");
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= m; ++j) 
            printf("%d%c", g[i][j], j == m ? '\n' : ' ');
    return 0;
}
View Code

 

 

备注:缺一道JOJ 2453,没找到提交地址?题目挺好的。
posted @ 2019-01-15 22:03  MJT12044  阅读(256)  评论(0编辑  收藏  举报