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

    飞行员配对

    (dinic) 跑二分图最大匹配,然后输出方案

    const int N = 300 + 5;
    const int M = 100000;
    int head[N], ver[M], nxt[M], edge[M], d[N];
    int n, m, s, t, tot, maxflow;
    queue<int> q;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x],head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    bool bfs(){
        memset(d, 0, sizeof d);
        while(q.size())q.pop();
        q.push(s);d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t)return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i&&rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    
    int main() {
        scanf("%d%d",&m,&n);
        tot = 1;
        int x, y, z;
        while(scanf("%d%d",&x,&y) == 2 && x != -1 && y != -1){
            add(x, y, 1);
        }
        s = n + m + 1, t = n + m + 2;
        for(int i=1;i<=m;i++)add(s, i, 1);
        for(int i=m+1;i<=m+n;i++) add(i, t, 1);
        int flow = 0;
        while(bfs()) while(flow = dinic(s, inf)) maxflow += flow;
        printf("%d
    ",maxflow);
        for(int i=1;i<=m;i++){
            for(int j=head[i];j;j=nxt[j]){
                if(ver[j] == s || edge[j])continue;
                printf("%d %d
    ",i, ver[j]);
                break;
            }
        }
        return 0;
    }
    

    圆桌问题

    s 向每个桌子连流量为(c[i]) 的边,每个桌子向每个团队连流量为 (1) 的边,每个团队向 (t) 连流量为 (r[i]) 的边。跑最大流

    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 500 + 5;
    const int M = 200000;
    int head[N], ver[M], edge[M], nxt[M], d[N];
    int r[N], c[N];
    int n, m, s, t, tot, maxflow;
    queue<int> q;
    void add(int x, int y, int z){
        ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;
        ver[++tot]=x,edge[tot]=0,nxt[tot]=head[y],head[y]=tot;
    }
    bool bfs(){
        while(q.size())q.pop();
        memset(d, 0, sizeof d);
        d[s] = 1;q.push(s);
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t)return true;
                }
            }
        }
        return false;
    }
    
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(edge[i], flow));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    
    int main() {
        scanf("%d%d",&m,&n);
        int sum = 0;
        tot = 1;
        for(int i=1;i<=m;i++)scanf("%d",&r[i]),sum+=r[i];
        for(int i=1;i<=n;i++)scanf("%d",&c[i]);
        s = n + m + 1, t = n + m + 2;
        for(int i=1;i<=n;i++) add(s, i, c[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) add(i,j+n,1);
        for(int i=1;i<=m;i++) add(i+n, t, r[i]);
        int flow = 0;
        while(bfs())while(flow = dinic(s, inf)) maxflow += flow;
        if(maxflow != sum){
            puts("0");return 0;
        }
        puts("1");
        for(int x=n+1;x<=n+m;x++){
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] == 0)continue;
                printf("%d ", ver[i]);
            }
            puts("");
        }
        return 0;
    }
    

    最小路径覆盖问题

    拆点,建图,跑最大流

    const int inf = 0x3f3f3f3f;
    const int N = 400 + 5;
    const int M = 20010;
    int head[N], ver[M], edge[M], nxt[M], d[N];
    int vis[N];
    int n, m, s, t, tot, maxflow;
    queue<int> q;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    bool bfs(){
        memset(d, 0, sizeof d);
        while(q.size())q.pop();
        q.push(s);d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    q.push(ver[i]);
                    d[ver[i]] = d[x] + 1;
                    if(ver[i] == t) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(int x, int flow){
        if(x == t) return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest; i=nxt[i]){
           	if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i ^ 1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    
    void get(int x){
        int t = x;
        bool flag;
        do{
            printf("%d ", t);
            vis[t] = 1;
            flag = false;
            for(int i=head[t];i;i=nxt[i]){
                if(ver[i] == s || edge[i])continue;
                t = ver[i] - n;
                flag = true;
                break;
            }
        }while(flag);
        puts("");
    }
    int main() {
        scanf("%d%d",&n,&m);
        tot = 1;
        for(int i=1;i<=m;i++){
            int x,y;scanf("%d%d",&x,&y);
            add(x, y + n, 1);
        }
        s = 2 * n + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            add(s, i, 1);
            add(i+n, t, 1);
        }
        int flow = 0;
        while(bfs())
            while(flow = dinic(s, inf)) maxflow += flow;
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                get(i);
            }
        }
        printf("%d
    ", n - maxflow);
        return 0;
    }
    

    最长不下降子序列

    特判n=1的情况

    (d[i]) 为以 i 结尾的最长不下降子序列长度

    (d[i] == 1), 则 (s -> i) 连一条边

    (d[j] + 1 == d[i] ~& ~ a[j] <= a[i]),则 $j' -> i $连一条边

    (d[i] == maxlen), 则$ i‘ -> t$ 连一条边

    $i -> i' $连一条边

    边权都为1,跑最大流

    第三问要修改 ((s, 1), (1,1'), (n, n'), (n', t)) 边权为(infin)

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 1000 + 5;
    const int M = 600000;
    int head[N], ver[M], edge[M], nxt[M], tot;
    int d[N],a[N],v[N];
    int n, s, t, maxflow;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    queue<int> q;
    bool bfs(){
        memset(d, 0, sizeof d);
        memset(v, 0, sizeof v);
        while(q.size())q.pop();
        q.push(s), d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        int mx = 0;
        tot = 1;
        for(int i=1;i<=n;i++){
            d[i] = 1;
            for(int j=1;j<i;j++){
                if(a[i] >= a[j]) {
                    d[i] = max(d[i], d[j]+1);
                }
            }
            mx = max(mx, d[i]);
        }
        s = 2 * n + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            if(d[i] == 1) add(s, i, 1);
            if(d[i] == mx) add(i+n, t, 1);
            add(i, i+n, 1);
            for(int j=1;j<i;j++){
                if(a[i] >= a[j] && d[i] == d[j] + 1){
                    add(j+n, i, 1);
                }
            }
        }
        int flow = 0;
        while(bfs()) while(flow = dinic(s, inf)) maxflow += flow;
        printf("%d
    ", mx);
        printf("%d
    ", maxflow);
        for(int i=head[s];i;i=nxt[i])if(ver[i] == 1) edge[i] = inf;
        for(int i=head[1];i;i=nxt[i])if(ver[i] == 1+n) edge[i] = inf;
        for(int i=head[n];i;i=nxt[i]) if(ver[i] == 2*n) edge[i] = inf;
        for(int i=head[2*n];i;i=nxt[i]) if(ver[i] == t) edge[i] = inf;
        while(bfs()) while(flow = dinic(s, inf)) maxflow += flow;
        if(n == 1) maxflow = 1;
        printf("%d
    ", maxflow);
        return 0;
    }
    

    方格取数(最大点权独立集)

    1. 满足每一条边的两个端点至少选一个的最小权点集
    2. 满足每一条边的两个端点至多选一个是最大权点集

    这两个集合互为补集,二分图左部点与 s 连边权为该点点权的边,右部与 t 连边权为该点点权的点,原二分图的边边权设置为(infin) ,跑最小割(最大流),被割掉的那些边,就是不能选的边(即一一对应原来二分图中的点)。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 10000 + 5;
    const int M = 100000;
    int head[N], ver[M], edge[M], nxt[M], tot;
    int d[N], a[101][101],v[N];
    int n, m, s, t, maxflow;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    queue<int> q;
    bool bfs(){
        memset(d, 0, sizeof d);
        memset(v, 0, sizeof v);
        while(q.size())q.pop();
        q.push(s), d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    inline int id(int x, int y){return (x-1)*m+y;}
    
    int main() {
        scanf("%d%d",&n,&m);
        s = n * m + 1, t = s + 1;
        tot = 1;
        int sum = 0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
                if((i+j)%2 == 0){
                    add(s, id(i, j), a[i][j]);
                    if(i > 1) add(id(i, j), id(i-1, j), inf);
                    if(j > 1) add(id(i, j), id(i, j-1), inf);
                    if(i < n) add(id(i, j), id(i+1, j), inf);
                    if(j < m) add(id(i, j), id(i, j+1), inf);
                }
                else add(id(i, j), t, a[i][j]);
                
                sum += a[i][j];
            }
        }
        int flow = 0;
        while(bfs()) while(flow = dinic(s, inf)) maxflow += flow;
        sum -= maxflow;
        printf("%d
    ",sum);
        return 0;
    }
    /*
    3 3
    1 2 3
    3 2 3
    2 3 1 
    */
    

    太空飞行计划问题

    最大权闭合图,选谁必选谁

    S 向正权点连容量为该点权的边, 负权点向T连容量为该点权绝对值的边,原来的边容量全部为(infin)

    最小割之后,与 S 联通点都要选,与T联通的点都不选

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <sstream>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 100 + 5;
    const int M = 100010;
    int head[N], ver[M], edge[M], nxt[M], tot;
    int d[N], v[N];
    int n, m, s, t, maxflow;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    queue<int> q;
    bool bfs(){
        memset(d, 0, sizeof d);
        memset(v, 0, sizeof v);
        while(q.size())q.pop();
        q.push(s), d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    cout << x << ' ' << ver[i] << endl;
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    int main() {
        scanf("%d%d",&n,&m);
        getchar();
        tot = 1;s = n + m + 1, t = s + 1;
        int sum = 0;
        for(int i=1;i<=n;i++){
            string t;
            getline(cin, t);
            stringstream ss(t);
            bool flag = true;
            int x = 0;
            while(ss >> x){
                if(flag) {
                    sum += x;
                    add(s, i, x);
                }
                else{
                    add(i, x+n, inf);
                }
                flag = false;
            }
        }
        for(int i=1;i<=m;i++){
            int x;scanf("%d",&x);
            add(i+n, t, x);
        }
        int flow = 0;
        while(bfs())while(flow = dinic(s, inf)) maxflow += flow;
        sum -= maxflow;
        //可以利用最后一次bfs的信息判断是否联通
        bool flag = true;
        for(int i=1;i<=n;i++) if(d[i] && flag) printf("%d",i),flag = false;else if(d[i]) printf(" %d",i);
        puts("");
        flag = true;
        for(int i=1;i<=m;i++) if(d[i+n] && flag) printf("%d",i),flag = false;else if(d[i+n]) printf(" %d",i);
        puts("");
        printf("%d
    ",sum);
        return 0;
    }
    

    负载均衡问题

    裸的均分纸牌问题,可以(O(nlog n)) 时间解决

    网络流的可以这样考虑,设平均值为 (ave)

    对于权值大于等于 (ave) 的点,连接 (s ightarrow i) ,边权为 (a[i] -ave) ,费用(0)

    对于权值小于等于(ave) 的点,连接(i ightarrow t) , 边权为(ave-a[i]), 费用 (0)

    对于相邻的点:连接(i ightarrow j) ,边权(inf), 费用为 (1)

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 100000 + 5;
    
    int n, a[N], s[N];
    
    int main() {
        cin >> n;
        int sum = 0;
        for(int i=1;i<=n;i++) cin >> a[i], sum += a[i];
        sum /= n;
        for(int i=1;i<=n;i++) a[i] -= sum,s[i] = s[i-1] + a[i];
        sort(s + 1, s + 1 + n);
        int res = 0;
        for(int i=1;i<=n;i++) res += abs(s[i] - s[(n+1)/2]);
        cout << res << endl;
        return 0;
    }
    

    软件补丁问题

    (spfa)或者(dijkstra)

    边数过多可以不加边,直接在转移时判断

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 20;
    const int M = 101;
    int n, m, s, t;
    int d[1<<20], v[1<<20];
    int c[M], b1[M], b2[M], f1[M], f2[M];
    char str[101];
    void dijkstra(){
        memset(d, 0x3f,sizeof d);
        priority_queue<pair<int,int> > q;
        q.push({0, s});d[s] = 0;
        while(q.size()){
            int x = q.top().second;q.pop();
            if(v[x])continue;
            v[x] = 1;
            for(int i=1;i<=m;i++){
                if((x&b1[i]) == b1[i] && (x&b2[i]) == 0){
                    int y = ((x|f1[i])^f1[i]) | f2[i];
                    if(d[y] > d[x] + c[i]){
                        d[y] = d[x] + c[i];
                        q.push({-d[y], y});
                    }
                }
            }
        }
    }
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d",&c[i]);
            scanf("%s",str);
            for(int j=0;j<n;j++)if(str[j] == '+')b1[i] |= 1 << j;else if(str[j] == '-') b2[i] |= 1 << j;
            scanf("%s",str);
            for(int j=0;j<n;j++)if(str[j] == '-')f1[i] |= 1 << j;else if(str[j] == '+') f2[i] |= 1 << j;
        }
        s = (1 << n) - 1;
        dijkstra();
        if(d[t] == inf)puts("0");
        else printf("%d
    ",d[t]);
        return 0;
    }
    

    孤岛营救问题

    (e[x1][y1][x2][y2]) 表示((x1,y1) ightarrow (x2,y2)) 门的情况,-1表示畅通,0表示不可逾越,否则状压表示所需的钥匙

    (d[x][y][i]) 表示到达((x,y)) 位置,拥有钥匙状态为 i 时的最短时间

    (key[x][y]) 表示 ((x,y)) 位置的钥匙情况

    由于转移条件每次只会增加1的时间,所以可以直接(bfs)

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 11;
    int e[N][N][N][N], key[N][N];
    int d[N][N][1<<N], v[N][N][1<<N];
    int n, m, p, k, s;
    queue<pair<pair<int,int>,int>>q;
    int dx[4] = {0,0,-1,1}, dy[4] = {1,-1,0,0};
    #define mk make_pair
    int dijkstra(){
        memset(d, 0x3f, sizeof d);
        q.push(mk(mk(1,1),key[1][1]));
        d[1][1][key[1][1]] = 0;
        while(q.size()){
            auto t = q.front();q.pop();
            int x = t.first.first, y = t.first.second, keys = t.second;
            for(int i=0;i<4;i++){
                int nx = x + dx[i], ny = y + dy[i];
                if(nx < 1 || nx > n || ny < 1 || ny > m) continue;
                if(e[x][y][nx][ny] == 0)continue;
                if(e[x][y][nx][ny] > 0 && (keys & e[x][y][nx][ny]) == 0)continue;
                int nkeys = keys | key[nx][ny];
                //printf("(%d,%d,%d)->(%d,%d,%d)
    ",x,y,keys,nx,ny,nkeys);
                if(v[nx][ny][nkeys])continue;
                d[nx][ny][nkeys] = d[x][y][keys] + 1;
                v[nx][ny][nkeys] = 1;
                q.push(mk(mk(nx,ny),nkeys));
                if(nx == n && ny == m) return d[nx][ny][nkeys];
            }
        }
        return -1;
    }
    int main() {
        scanf("%d%d%d%d",&n,&m,&p,&k);
        memset(e, -1, sizeof e);
        for(int i=1;i<=k;i++){
            int x1,y1,x2,y2,t;
            scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&t);
            if(t == 0) 
                e[x1][y1][x2][y2] = e[x2][y2][x1][y1] = t;
            else e[x1][y1][x2][y2] = e[x2][y2][x1][y1] = 1 << (t-1);
        }
        scanf("%d",&s);
        for(int i=1;i<=s;i++){
            int x, y, t;
            scanf("%d%d%d",&x,&y,&t);
            key[x][y] |= 1 << (t-1);
        }
        printf("%d
    ",dijkstra());
        return 0;
    }
    

    星际转移

    首先用并查集判断是否联通,然后确定有没有解。

    猜想答案不会很大,所以每次增加一次时间,就多建一层图,从源点向所有的地球连inf的边,由于人类可以停留不动,所以上一层到这一层的对应节点之间连inf的边,然后上一层与这一层可以发生太空船转移的,加h[i]容量的边

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 1000000 + 5;
    const int M = 1000010;
    int head[N], ver[M], nxt[M], edge[M], d[N], tot;
    int f[50], h[50], num[50], g[50][50]; 
    int n, m, k, s, t;
    int maxflow, flow, res;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    int find(int x){return x == f[x] ? x : f[x] = find(f[x]);}
    void merge(int x, int y){
        x = find(x), y = find(y);
        if(x != y) f[x] = y;
    }
    bool bfs(){
        for(int i=0;i<=res*(n+1);i++) d[i] = 0;
        d[t] = 0;
        queue<int> q;
        q.push(s);d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    q.push(ver[i]);
                    if(ver[i] == t) return true;
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t) return flow;
        int rest = flow, k;
        for(int i=head[x];i&&rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(edge[i], rest));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i^1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    int main() {
        scanf("%d%d%d",&n,&m,&k);
        s = 0, t = 1000000;
        for(int i=1;i<=n+2;i++)f[i] = i;
        for(int i=1;i<=m;i++){
            scanf("%d%d",&h[i], &num[i]);
            for(int j=0;j<num[i];j++){
                scanf("%d",&g[i][j]);
                if(g[i][j] == -1) g[i][j] = n+2;
                if(g[i][j] == 0) g[i][j] = n+1;
                if(j) merge(g[i][j-1], g[i][j]);
            }
        }
        if(find(n+1) != find(n+2)){
            puts("0");
            return 0;
        }
        tot = 1;
        for(res=1;;res++){
            add(s, res * (n+1), inf);
            for(int i=1;i<=m;i++){
                int x = (res-1)%num[i], y = res % num[i];
                if(g[i][x] == n+2) x = t;
                else x = (res-1)*(n+1) + g[i][x];
                if(g[i][y] == n+2) y = t;
                else y = res*(n+1) + g[i][y];
                add(x, y, h[i]);
            }
            while(bfs()) while(flow = dinic(s, inf)) maxflow += flow;
            if(maxflow >= k){
                printf("%d
    ", res);
                return 0;
            }
            for(int i=1;i<=n+1;i++)add((res-1)*(n+1)+i, res*(n+1)+i, inf);
        }
        return 0;
    }
    

    餐巾计划

    每一天要产生 (r_i) 个脏餐巾 -> s向i连流量为(r_i),费用为0的边

    每一天要使用 (r_i) 个餐巾 -> i'向t连流量为(r_i),费用为0的边

    每一天可以购买餐巾,s 向 i' 连流量为inf,费用为p的边

    脏的餐巾可以送到快洗店 -> i 向 (i+m)' 连流量为inf,费用为f的边

    脏的餐巾可以送到慢洗店 -> i 向 (i+n)' 连流量为inf,费用为s的边

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 4000 + 5;
    const int M = 1000010;
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int pre[N], d[N], incf[N],  v[N];
    int n, k, tot, s, t, p, kx, mx, fkx, fmx;
    ll maxflow, ans;
    void add(int x,int y,int z,int c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0x3f, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] > d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == inf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += 1ll * d[t] * incf[t];
    }
    int main() {
        scanf("%d",&n);
        tot = 1, s = 2 * n + 1, t = s + 1;
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            add(s, i, x, 0);
            add(i+n, t, x, 0);
        }
        scanf("%d%d%d%d%d",&p,&kx,&fkx,&mx,&fmx);
        for(int i=1;i<=n;i++){
            add(s, i+n, inf, p);
            if(i < n) add(i, i+1, inf, 0);
            if(i + kx <= n) add(i, i+kx+n, inf, fkx);
            if(i + mx <= n) add(i, i+mx+n, inf, fmx);
        }
        while(spfa())update();
        printf("%lld
    ",ans);
        return 0;
    }
    

    试题库问题

    s 向每种类别连边,流量为其所需的题目数量

    每种类别向对应的题目连边,流量为 1

    每个题目向 t 连边,流量为 1

    跑最大流,如果流量等于所需题目总数则输出方案,否则输出No Solution!

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 2000 + 5;
    const int M = 100010;
    int head[N], ver[M], edge[M], nxt[M], d[N];
    int n, m, k, s, t, tot, maxflow;
    queue<int> q;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    bool bfs(){
        memset(d, 0, sizeof d);
        while(q.size())q.pop();
        q.push(s);d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    q.push(ver[i]);
                    d[ver[i]] = d[x] + 1;
                    if(ver[i] == t) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(int x, int flow){
        if(x == t) return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest; i=nxt[i]){
           	if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i ^ 1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    
    int main() {
        scanf("%d%d",&k,&n);
        s = k + n + 1, t = s + 1;
        tot = 1;
        for(int i=1,x;i<=k;i++){
            scanf("%d",&x);
            add(s, i, x);
            m+=x;
        }
        for(int i=1,p,x;i<=n;i++){
            scanf("%d",&p);
            add(i+k, t, 1);
            while(p--){
                scanf("%d",&x);
                add(x, i+k, 1);
            }
        }
        int flow = 0;
        while(bfs())while(flow = dinic(s,inf))maxflow += flow;
        if(maxflow < m){
            puts("No Solution!");
            return 0;
        }
        for(int i=1;i<=k;i++){
            printf("%d:", i);
            for(int j = head[i];j;j=nxt[j]){
                if(ver[j] != s && edge[j] == 0){
                    printf(" %d", ver[j] - k);
                }
            }
            puts("");
        }
        return 0;
    }
    

    最长k可重线段集问题

    此题与最长 k 可重区间集问题有很大的相似之处,唯有一点不同的是,线段可以垂直于 x 轴,所以我们不能让 x -> x连边产生自环,所以对点进行拆点,每个点分为入状态和出状态两个状态。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 2000 + 5;
    const int M = 1000010;
    int n, k, l[N], r[N], l1[N], r1[N];
    int ver[M], edge[M], nxt[M], head[N];
    int incf[N], pre[N], v[N];
    int tot, s, t;
    ll cost[M], d[N], maxflow, ans, len[N];
    vector<int> all;
    void add(int x,int y,int z,ll c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0x3f, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] > d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == 0x3f3f3f3f3f3f3f3f)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    ll S(ll x){return x * x;}
    ll calc(int id){
        return sqrt(S(l[id]-l1[id]) + S(r[id]-r1[id]));
    }
    int main() {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d%d%d%d",&l[i],&r[i],&l1[i], &r1[i]);
            if(l[i] > l1[i]) swap(l[i], l1[i]);
            len[i] = calc(i);
            l[i] <<= 1;
            l1[i] <<= 1;
            if(l[i] == l1[i]) l1[i]++; else l[i] ++;//这里的操作就是拆点操作,奇数点为出点,偶数点为入点
            all.push_back(l[i]);all.push_back(l1[i]);
        }
    
        sort(all.begin(),all.end());
        tot = 1, s = all.size(), t = s + 1;
        add(s, 0, k, 0);
        add(s-1, t, k, 0);
        for(int i=0;i<all.size() - 1;i++){
            add(i, i + 1, inf, 0);
        }
        for(int i=1;i<=n;i++){
            int lid = lower_bound(all.begin(),all.end(),l[i]) - all.begin();
            int rid = lower_bound(all.begin(), all.end(), l1[i]) - all.begin();
            add(lid, rid, 1, -len[i]);
        }
        while(spfa())update();
        cout << -ans << endl;
        return 0;
    }
    

    最长k可重区间集问题

    设值域范围为 [a,b] ,则 s 向 a 连流量 k,费用0的边,b 向 t 连流量 k,费用 0 的边,从 s 走到 t,[a,b) 中的每个点 i ,都向 i+1 连流量为 inf,费用为 0 的边。对于每一个输入给定的区间 [l,r],l 向 r 连流量为 1,费用为 r-l 的边,然后跑最大费用最大流即可。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 2000 + 5;
    const int M = 1000010;
    int n, k, l[N], r[N], id[N];
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int d[N], incf[N], pre[N], v[N];
    int tot, s, t, maxflow, ans;
    vector<int> all;
    void add(int x,int y,int z,int c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0x3f, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] > d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == inf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    
    int main() {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&l[i],&r[i]);
            if(l[i] > r[i]) swap(l[i], r[i]);
            all.push_back(l[i]);all.push_back(r[i]);
        }
        sort(all.begin(),all.end());
        tot = 1, s = all.size(), t = s + 1;
        add(s, 0, k, 0);
        add(s-1, t, k, 0);
        for(int i=0;i<all.size() - 1;i++){
            add(i, i + 1, inf, 0);
        }
        for(int i=1;i<=n;i++){
            int lid = lower_bound(all.begin(),all.end(),l[i]) - all.begin();
            int rid = lower_bound(all.begin(), all.end(), r[i]) - all.begin();
            add(lid, rid, 1, l[i]-r[i]);
        }
        while(spfa())update();
        cout << -ans << endl;
        return 0;
    }
    

    火星探险问题

    将每个点拆为入点和出点,对于不是障碍的每个入点向出点连流量为 inf,费用为 0 的边,如果该点是岩石,则再连 流量为 1,费用为 1 的边

    s 连 (1,1) 的入点, 流量为 k,费用为 0

    (n,m) 的出点连 t 的出点,流量为 k, 费用 0

    最后要输出方案,从 s 走到 t 走最大流的个数次,每次从出点遍历边,当该边的反向边流量不为 1 时,则从该边去走,同时将该反向边的流量减 1

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 2500 + 5;
    const int M = 1000010;
    int head[N], ver[M], edge[M], cost[M], nxt[M];
    int d[N], incf[N], pre[N], v[N];
    int n, m, k, tot, s, t, maxflow, ans, sum;
    int a[50][50];
    inline int id(int x, int y){return x * m + y;}
    void add(int x, int y, int z, int c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0xcf, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for(int i=head[x];i;i=nxt[i]){
                if(!edge[i]) continue;
                int y = ver[i];
                if(d[y] < d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y]) v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == 0xcfcfcfcf)return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i^1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += incf[t] * d[t];
    }
    void print(int cur){
        int x = id(0,0) + sum;
        while(x != id(n-1, m-1) + sum){
            int y;
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i^1]){
                    edge[i^1]--;
                    y = ver[i];
                    break;
                }
            }
            if(y == x - sum + 1){ // 向右走
                printf("%d %d
    ", cur, 1);
            }
            else printf("%d %d
    ", cur, 0);
            x = y + sum;
        }
    }
    int main() {
        scanf("%d%d%d",&k,&m,&n);
        tot = 1, s = 2 * n * m + 1, t = s + 1;
        sum = n * m;
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                scanf("%d",&a[i][j]);
                if(a[i][j] == 0 || a[i][j] == 2) add(id(i,j), id(i,j) + sum, inf, 0);
                if(a[i][j] == 2) add(id(i,j), id(i,j) + sum, 1, 1);
            }
        }
        add(s, id(0, 0), k, 0);
        add(id(n-1, m-1) + sum, t, k, 0);
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(i < n-1) add(id(i,j)+sum, id(i+1,j), inf, 0);
                if(j < m-1) add(id(i,j)+sum, id(i,j+1), inf, 0);
            }
        }
        while(spfa()) update();
        //cout << maxflow << endl;
        for(int i=1;i<=maxflow;i++){
            print(i);
        }
        return 0;
    }
    

    深海机器人问题

    这个题建图稍微有些复杂,需要理清楚当前输入的哪条边,只要仔细建好图,这个题就是不难的。(给出的起点和终点坐标不只是反的还是正的,就直接面向数据编程了)

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 1000 + 5;
    const int M = 1000010;
    int P, Q, A, B;
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int d[N], incf[N], pre[N], v[N];
    int n, k, tot, s, t, maxflow, ans;
    void add(int x,int y,int z,int c){
        //dbg(x,y,z,c);
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    inline int num(int i, int j, int k) { return (i - 1) * n + j + k * n * n; }
    bool spfa(){
        queue<int> q;
        memset(d, 0xcf, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] < d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == 0xcfcfcfcf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    int id(int x, int y){
        return x * (Q+1) + y;
    }
    
    int main() {
        scanf("%d%d%d%d",&A,&B, &P, &Q);
        tot = 1;
        s = (P+1)*(Q+1), t = s + 1;
        for(int i=0;i<P+1;i++){
            for(int j=0;j<Q;j++){
                int x;scanf("%d",&x);
                add(id(i, j), id(i, j+1), 1, x);
                add(id(i, j), id(i, j+1), inf, 0);
            }
        }
        for(int j=0;j<Q+1;j++){
            for(int i=0;i<P;i++){
                int x;scanf("%d",&x);
                add(id(i,j), id(i+1,j), 1, x);
                add(id(i,j), id(i+1,j), inf, 0);
            }
        }
        for(int i=1;i<=A;i++){
            int k, x, y;
            scanf("%d%d%d",&k,&x,&y);
            //swap(x,y);
            add(s, id(x, y), k, 0);
        }
        for(int i=1;i<=B;i++){
            int k, x, y;
            scanf("%d%d%d",&k,&x,&y);
            //swap(x,y);
            add(id(x,y), t, k, 0);
        }
        while(spfa())update();
        printf("%d
    ",ans);
        return 0;
    }
    

    航空路线问题

    这个题可以理解成从s 到 t 找两个不交叉的最长路径,拆点,x 向 x' 连流量为1,费用为1的边,s 向 s' 连流量为2,费用为0的边,t 向 t' 连流量为2,费用为0的边,原图的边(u,v),从 u' 向 v 连流量为1,费用为0的边, 注意一点的是如果 u = s, v = t, 则该边流量要设置为 2。从 s 到 t跑最大费用最大流, 然后输出方案即可。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <unordered_map>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 200 + 5;
    const int M = 200010;
    unordered_map<string,int> mp;
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int d[N], incf[N], pre[N], v[N];
    int n, m, k, tot, s, t, maxflow, ans;
    string name[101];
    void add(int x,int y,int z,int c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    inline int num(int i, int j, int k) { return (i - 1) * n + j + k * n * n; }
    bool spfa(){
        queue<int> q;
        memset(d, 0xcf, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] < d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == 0xcfcfcfcf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    
    int main() {
        scanf("%d%d",&n,&m);
        tot = 1, s = 2 * n + 1, t = s + 1;
        add(s, 1+n, 2, 0);
        add(n, t, 2, 0);
        for(int i=1;i<=n;i++){
            cin >> name[i];
            mp[name[i]] = i;
            add(i, i+n, 1, 1);
        }
        for(int i=1;i<=m;i++){
            string x,y;cin >> x >> y;
            if(mp[x] > mp[y]) swap(x, y);
            if(mp[x] == 1 && mp[y] == n)
                add(mp[x] + n, mp[y], 2, 0);
            else add(mp[x] + n, mp[y], 1, 0);
        }
        while(spfa()) update();
        if(maxflow != 2){
            puts("No Solution!");
            return 0;
        }
        printf("%d
    ", ans + 2);
        cout << name[1] << endl;
        memset(v, 0, sizeof v);
        int p = 1 + n;
        while(p != n+n){
            for(int i=head[p];i;i=nxt[i]){
                if(edge[i] == 0 && ver[i] <= n && !v[ver[i]]){
                    v[ver[i]] = 1;
                    p = ver[i];break;
                }
            }
            cout << name[p] << endl;
            p = p + n;
        }
        p = n;
        while(p != 1){
            for(int i=head[p];i;i=nxt[i]){
                if(edge[i] != 0 && ver[i] <= 2 * n && !v[ver[i] - n]){
                    v[ver[i]-n] = 1;
                    p = ver[i] - n;break;
                }
            }
            cout << name[p] << endl;
        }
        return 0;
    }
    

    数字梯形问题

    洛谷区题解

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 1610;
    const int M = 1000010;
    int n, m, a[N][N], sum, id[N][N];
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int d[N], incf[N], pre[N], v[N];
    int tot, s, t, maxflow, ans;
    void add(int x,int y,int z,int c){
        //dbg(x,y,z,c);
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0xcf, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] < d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        //cout << d[t] << endl;
        if(d[t] == 0xcfcfcfcf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    
    void clear(){
        memset(head, 0, sizeof head);
        maxflow = 0;
        tot = 1;
    }
    void solve1(){
        clear();
        s = 2 * sum + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            for(int j = 1; j <= m + i - 1; j++){
                if(i == 1) add(s, id[i][j], 1, 0);
                add(id[i][j], id[i][j]+sum, 1, a[i][j]);
                if(i < n){
                    add(id[i][j] + sum, id[i+1][j], 1, 0);
                    add(id[i][j] + sum, id[i+1][j+1], 1, 0);
                }else add(id[i][j]+sum, t, 1, 0);
            }
        }
        //for(int i=1;i<=m+n-1;i++) add(id[n][i] + sum, t, 1, 0);
        ans = 0;
        while(spfa()) update();
        printf("%d
    ",ans);
    }
    void solve2(){
        clear();
        s = sum + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m+i-1;j++){
                if(i == 1) add(s, id[i][j], 1, a[i][j]);
                if(i < n) {
                    add(id[i][j], id[i+1][j], 1, a[i+1][j]);
                    add(id[i][j], id[i+1][j+1], 1, a[i+1][j+1]);
                } else {
                    add(id[i][j], t, inf, 0);
                }
            }
        }
        ans = 0;
        while(spfa()) update();
        printf("%d
    ",ans);
    }
    void solve3(){
        clear();
        s = sum + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m+i-1;j++){
                if(i == 1) add(s, id[i][j], 1, a[i][j]);
                if(i < n) {
                    add(id[i][j], id[i+1][j], inf, a[i+1][j]);
                    add(id[i][j], id[i+1][j+1], inf, a[i+1][j+1]);
                } else {
                    add(id[i][j], t, inf, 0);
                }
            }
        }
        ans = 0;
        while(spfa()) update();
        printf("%d
    ",ans);
    }
    int main() {
        scanf("%d%d",&m,&n);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m+i-1;j++){
                scanf("%d",&a[i][j]);
                id[i][j] = ++sum;
            }   
        }
        solve1();
        solve2();
        solve3();
        return 0;
    }
    

    骑士共存问题

    互斥的位置,横坐标与纵坐标和的奇偶性不同,所以最终连接好的是一个二分图,将互斥关系建图,跑最大流求出最小割,含义是需要割掉多少条边使得不互斥,能割掉的只有从 s, 和 t 连接的边,所以把互斥关系的边流量设置为 inf,这样就可以表示要最多取消放置多少个骑士,使得图中不存在互斥关系。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int inf = 0x3f3f3f3f;
    const int N = 40000 + 5;
    const int M = 1000010;
    int n, m, a[202][202];
    int s, t, head[N], ver[M], nxt[M], edge[M], tot;
    int d[N],maxflow;
    
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    int dx[8] = {-1,-2,-2,-1,1,2,2,1};
    int dy[8] = {-2,-1,1,2,2,1,-1,-2};
    int id(int x, int y){return (x-1)*n + y;}
    void add(int x, int y){
        if(a[x][y])return;
        if((x+y) & 1){
            add(id(x,y), t, 1);
            return ;
        }
        add(s, id(x, y), 1);
        for(int i=0;i<8;i++){
            int nx = x + dx[i], ny = y + dy[i];
            if(nx < 1 || nx > n || ny < 1 || ny > n || a[nx][ny])continue;
            add(id(x,y), id(nx,ny), 1);
        }
    }
    bool bfs(){
        queue<int> q;
        memset(d,0,sizeof d);
        q.push(s), d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    d[ver[i]] = d[x] + 1;
                    if(ver[i] == t)return true;
                    q.push(ver[i]);
                }
            }
        }
        return false;
    }
    int dinic(int x, int flow){
        if(x == t)return flow;
        int rest = flow, k;
        for(int i=head[x];i&&rest;i=nxt[i]){
            if(edge[i] && d[ver[i]] == d[x] +1){
                k = dinic(ver[i], min(edge[i], rest));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i ^ 1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    int main() {
        scanf("%d%d",&n,&m);
        int sum = n * n;
        for(int i=1;i<=m;i++){
            int x,y;scanf("%d%d",&x,&y);
            a[x][y] = 1;
            sum --;
        }
        tot = 1, s = n * n + 1, t = s + 1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                add(i, j);
            }
        }
        int flow;
        while(bfs())while(flow=dinic(s, inf))maxflow+=flow;
        sum -= maxflow;
        printf("%d
    ", sum);
        return 0;
    }
    

    运输问题

    供需关系建图,跑最小费用最大流即可

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 200 + 5;
    const int M = 100010;
    int n,m,a[N], b[N];
    int ver[M], edge[M], cost[M], nxt[M], head[N];
    int d[N], incf[N], pre[N], v[N], e[N][N];
    int k, tot, s, t, maxflow, ans;
    void add(int x,int y,int z,int c){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot, cost[tot] = c;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot, cost[tot] = -c;
    }
    bool spfa(){
        queue<int> q;
        memset(d, 0x3f, sizeof d);
        memset(v, 0, sizeof v);
        q.push(s);
        d[s] = 0, v[s] = 1;
        incf[s] = inf;
        while(q.size()){
            int x = q.front();
            q.pop();
            v[x] = 0;
            for (int i = head[x]; i;i=nxt[i]){
                if(!edge[i])
                    continue;
                int y = ver[i];
                if(d[y] > d[x] + cost[i]){
                    d[y] = d[x] + cost[i];
                    incf[y] = min(incf[x], edge[i]);
                    pre[y] = i;
                    if(!v[y])
                        v[y] = 1, q.push(y);
                }
            }
        }
        if(d[t] == inf)
            return false;
        return true;
    }
    void update(){
        int x = t;
        while(x != s){
            int i = pre[x];
            edge[i] -= incf[t];
            edge[i ^ 1] += incf[t];
            x = ver[i ^ 1];
        }
        maxflow += incf[t];
        ans += d[t] * incf[t];
    }
    
    
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)scanf("%d",&b[i]);
        s = n + m + 1, t = s + 1;
        tot = 1;
        for(int i=1;i<=n;i++){
            add(s, i, a[i], 0);
            for(int j=1,x;j<=m;j++){
                scanf("%d",&e[i][j]);
                add(i, j+n, inf, e[i][j]);
            }
        }
        for(int i=1;i<=m;i++) add(i+n, t, b[i], 0);
        while(spfa()) update();
        printf("%d
    ", ans);
        memset(head, 0, sizeof head);
        tot = 1;
        for(int i=1;i<=n;i++){
            add(s, i, a[i], 0);
            for(int j=1,x;j<=m;j++){
                add(i, j+n, inf, -e[i][j]);
            }
        }
        for(int i=1;i<=m;i++) add(i+n, t, b[i], 0);
        ans = 0;
        while(spfa()) update();
        printf("%d
    ", -ans);
        return 0;
    }
    

    汽车加油行驶问题

    这是个最短路的题,d[x][y][i] 表示从起点到 (x,y) ,油量为i 的最小花费。

    注意一点的是如果到达了一个有加油站的地点,那么这时的操作必须要加油,加满了之后才能接着走。

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 100 + 5;
    int a[N][N], n, k, A, B, C;
    int d[N][N][11], v[N][N][11];
    struct node{
        int x, y, k;
        node(int x,int y, int k):x(x),y(y),k(k){}
    };
    bool operator <(const node a, const node b){
        return a.x < b.x;    
    }
    #define mk make_pair
    priority_queue<pair<int,node>>q;
    int dx[4] = {-1,0,1,0}, dy[4] = {0, -1, 0, 1};
    int dijkstra(){
        memset(d, 0x3f, sizeof d);
        memset(v, 0, sizeof v);
        q.push(mk(0, (node){1,1,k}));
        d[1][1][k] = 0;
        while(q.size()){
            auto t = q.top().second;q.pop();
            int x = t.x, y = t.y, ck = t.k;
            if(v[x][y][ck])continue;
            v[x][y][ck] = 1;
            if(ck < k){
                if(a[x][y] == 0){
                    if(d[x][y][k] > d[x][y][ck] + A + C){//如果这个地方没有加油站,可以建一个加油站
                        d[x][y][k] = d[x][y][ck] + A + C;
                        q.push(mk(-d[x][y][k], (node){x,y,k}));
                    }
                }else{
                    if(d[x][y][k] > d[x][y][ck] + A){//加油操作
                        d[x][y][k] = d[x][y][ck] + A;
                        q.push(mk(-d[x][y][k], (node){x,y,k}));
                    }
                    continue;//这里要continue
                }
            }
            if(ck)
            for(int i=0;i<4;i++){
                int nx = x + dx[i], ny = y + dy[i];
                if(nx < 1 || nx > n || ny < 1 || ny > n)continue;
                int cost = d[x][y][ck];
                if(i < 2) cost += B;
                if(d[nx][ny][ck-1] > cost){
                    d[nx][ny][ck-1] = cost;
                    q.push(mk(-d[nx][ny][ck-1], (node){nx,ny,ck-1}));
                }
            }
        }
        int res = inf;
        for(int i=0;i<=k;i++) res = min(res, d[n][n][i]);
        return res;
    }
    
    int main() {
        scanf("%d%d%d%d%d",&n,&k,&A,&B,&C);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
        }  
        printf("%d
    ", dijkstra());
        return 0;
    }
    

    分配问题

    二分图带权最大匹配问题,可以用费用流解决但是复杂度比较玄学,如果边数太多很可能会卡((M = N^2)),边数在500以下时完全可以跑KM,但是最好用BFS版本的KM,复杂度为 (O(n^3)) , DFS版本的KM复杂度一般都是(O(N^4))

    可以参考https://www.cnblogs.com/1625--H/p/12468117.html 中的 J-spy

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 100 + 5;
    ll n, a[N],b[N],c[N],p[N];
    ll w[N][N];
    ll lx[N] , ly[N];
    ll match[N];
    ll slack[N];
    bool vy[N];
    ll pre[N];
    void bfs( ll k ){
        ll x , y = 0 , yy = 0 , delta;
        memset( pre , 0 , sizeof(pre) );
        for( ll i = 1 ; i <= n ; i++ ) slack[i] = inf;
        match[y] = k;
        while(1){
            x = match[y]; delta = inf; vy[y] = true;
            for( ll i = 1 ; i <= n ;i++ ){
                if( !vy[i] ){
                    if( slack[i] > lx[x] + ly[i] - w[x][i] ){
                        slack[i] = lx[x] + ly[i] - w[x][i];
                        pre[i] = y;
                    }
                    if( slack[i] < delta ) delta = slack[i] , yy = i ;
                }
            }
            for( ll i = 0 ; i <= n ; i++ ){
                if( vy[i] ) lx[match[i]] -= delta , ly[i] += delta;
                else slack[i] -= delta;
            }
            y = yy ;
            if( match[y] == -1 ) break;
        }
        while( y ) match[y] = match[pre[y]] , y = pre[y];
    }
     
    ll KM(){
        memset( lx , 0 ,sizeof(lx) );
        memset( ly , 0 ,sizeof(ly) );
        memset( match , -1, sizeof(match) );
        for( ll i = 1 ; i <= n ; i++ ){
            memset( vy , false , sizeof(vy) );
            bfs(i);
        }
        ll res = 0 ;
        for( ll i = 1 ; i <= n ; i++ ){
            if( match[i] != -1 ){
                res += w[match[i]][i] ;
            }
        }
        return res;
    }
     
    int main()
    {
        scanf("%lld",&n);
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%lld",&w[i][j]);
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)w[i][j] = -w[i][j];
        printf("%lld
    ",-KM());
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)w[i][j] = -w[i][j];
        printf("%lld
    ",KM());
        return 0;
    }
    

    魔术球问题

    可以想到最终答案不会特别大,因为完全平方数到后面会非常稀疏

    所以每次新增一个球,将它与之前可以组合为完全平方数的球连边,在残余网络中跑最大流,当球数量 - 最大流 > n 时,就停止

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int N = 20000 + 5;
    const int maxn = 10000;
    const int M = 200010;
    int head[N], ver[M], edge[M], nxt[M], d[N];
    int vis[N];
    int n, m, s, t, tot, maxflow, res;
    queue<int> q;
    void add(int x, int y, int z){
        ver[++tot] = y, edge[tot] = z, nxt[tot] = head[x], head[x] = tot;
        ver[++tot] = x, edge[tot] = 0, nxt[tot] = head[y], head[y] = tot;
    }
    bool bfs(){
        memset(d, 0, sizeof d);
        while(q.size())q.pop();
        q.push(s);d[s] = 1;
        while(q.size()){
            int x = q.front();q.pop();
            for(int i=head[x];i;i=nxt[i]){
                if(edge[i] && !d[ver[i]]){
                    q.push(ver[i]);
                    d[ver[i]] = d[x] + 1;
                    if(ver[i] == t) return 1;
                }
            }
        }
        return 0;
    }
    int dinic(int x, int flow){
        if(x == t) return flow;
        int rest = flow, k;
        for(int i=head[x];i && rest; i=nxt[i]){
           	if(edge[i] && d[ver[i]] == d[x] + 1){
                k = dinic(ver[i], min(rest, edge[i]));
                if(!k) d[ver[i]] = 0;
                edge[i] -= k;
                edge[i ^ 1] += k;
                rest -= k;
            }
        }
        return flow - rest;
    }
    bool isok(int x){
        int s = sqrt(x);
        return s * s == x;
    }
    void get(int x){
        int t = x;
        bool flag;
        do{
            vis[t] = 1;printf("%d ", t);
            flag = false;
            for(int i=head[t];i;i=nxt[i]){
                if(ver[i] == s || vis[i] == res || edge[i])continue;
                flag = true;t = ver[i]-maxn;break;
            }
        }while(flag);
        puts("");
    }
    int main() {
        scanf("%d",&n);
        s = 0, t = 20000;
        tot = 1;
        for(int i=1;;i++){
            add(s, i, 1); add(i+maxn, t, 1);
            for(int j=1;j<i;j++){
                if(isok(i+j)) add(j, i + maxn, 1);
            }
            int flow = 0;
            while(bfs())while(flow = dinic(s, inf)) maxflow += flow;
            if(i - maxflow > n){
                res = i - 1;break;
            }
        }
        printf("%d
    ",res);
        for(int i=1;i<=res;i++){
            if(!vis[i])get(i);
        }
        return 0;
    }
    
  • 相关阅读:
    C# lock
    read appSettings in configuration file by XElement with xmlns
    get all sites under IIS
    Cross-site scripting(XSS)
    Do not throw System.Exception, System.SystemException, System.NullReferenceException, or System.IndexOutOfRangeException intentionally from your own source code
    C++ 算法
    C++ 数据结构概念
    C++ STL 常用算术和生成算法
    C++ STL 常用拷贝和替换算法
    C++ STL 常用排序算法
  • 原文地址:https://www.cnblogs.com/1625--H/p/12498234.html
Copyright © 2011-2022 走看看