zoukankan      html  css  js  c++  java
  • 网络流专题

    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 << "
    ";
        ++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 << "
    ";
        ++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
    ", 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
    ", 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
    ", 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
    ", 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 << "
    ";
        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
    ", 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 << "
    ";
    }
    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
    ", 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
    ", (ans * cnt0 - sum0));
        }
        else {
            LL k = (sum0 - sum1) / (cnt0 - cnt1);
            if (k >= Mx && check(k)) printf("%lld
    ", (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
    ", 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
    ", 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
    ", 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
    ", 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 ? '
    ' : ' ');
        return 0;
    }
    View Code
    备注:缺一道JOJ 2453,没找到提交地址?题目挺好的。
  • 相关阅读:
    品Spring:真没想到,三十步才能完成一个bean实例的创建
    品Spring:对@Autowired和@Value注解的处理方法
    品Spring:对@Resource注解的处理方法
    品Spring:对@PostConstruct和@PreDestroy注解的处理方法
    品Spring:详细解说bean后处理器
    品Spring:bean工厂后处理器的调用规则
    品Spring:注解之王@Configuration和它的一众“小弟们”
    品Spring:SpringBoot发起bean定义注册的“二次攻坚战”
    品Spring:SpringBoot轻松取胜bean定义注册的“第一阶段”
    .net的retrofit--WebApiClient底层篇
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10274547.html
Copyright © 2011-2022 走看看