最大流--模板

题意:问能否构造一个矩阵,使得矩阵中的每个数字范围是1-20并且满足矩阵每行之和与矩阵每列之和与所给数组相等。

思路:尽管矩阵是20*20 ,但是如果爆搜复杂度依旧爆炸,我们考虑使用网络流解决这个问题,下面重点讲解怎么构图。至于怎么写可以参考紫书,与网上的模板。

建图:先来考虑本题的限制是什么,首先i行与j列 存在着一个关系 即共用元素k[i][j],如果我们把数组A[i]B[j]分别看成节点,那么意味着A[i]可以流向B[j]大小最多为k[i][j]的流量至少为1的流量,注意到,网络流中流量最小为0,所以我们可以先给每个元素减1 同时算出减完后的A’[i]B’[j] 求出这个问题下的k’[i][j] 之后k[i][j]=k[i][j]’+1 即可,我们现在可以给A’[i]连一条通向B’[j]的容量为19的边,同时建立一个原点s ,它与每个A’[i]节点连一条边 容量为A’[i]的值 ,建立一个汇点t ,每个B’[j]连一条通向t的边,容量为 B’[j] 跑一遍最大流,如果汇点t的流量之和为sum(B[j])(j from 1 to m) 那么意味可以构造出来 并且k’[i][j] = A’[i]B’[j]的流量

//主代码来自刘汝佳
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 50 + 5;
const int INF = 1000000000;

struct Edge {
    int from, to, cap, flow;
    Edge(int u, int v, int c, int f) :from(u), to(v), cap(c), flow(f) {}
};

struct EdmondsKarp {
    int n, m;
    vector<Edge> edges;    // 边数的两倍
    vector<int> G[maxn];   // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
    int a[maxn];           // 当起点到i的可改进量
    int p[maxn];           // 最短路树上p的入弧编号

    void init(int n) {
        for (int i = 0; i < n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    int Maxflow(int s, int t) {
        int flow = 0;
        for (;;) {
            memset(a, 0, sizeof(a));
            queue<int> Q;
            Q.push(s);
            a[s] = INF;
            while (!Q.empty()) {
                int x = Q.front(); Q.pop();
                for (int i = 0; i < G[x].size(); i++) {
                    Edge& e = edges[G[x][i]];
                    if (!a[e.to] && e.cap > e.flow) {
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x], e.cap - e.flow);
                        Q.push(e.to);
                    }
                }
                if (a[t]) break;
            }
            if (!a[t]) break;
            for (int u = t; u != s; u = edges[p[u]].from) {
                edges[p[u]].flow += a[t];
                edges[p[u] ^ 1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
};

EdmondsKarp g;
int no[maxn][maxn];
int a[maxn], b[maxn], str[maxn][maxn];

int main() {
    int R, C, v, kase = 0;
    scanf("%d%d", &R, &C);
    g.init(R + C + 2);
    for (int i = 1; i <= R; i++) {
        scanf("%d", &v);
        g.AddEdge(0, i, v - C); // row sum is v - last
        a[i] = v;
    }
    int sum_b = 0;
    for (int i = 1; i <= C; i++) {
        scanf("%d", &v);
        g.AddEdge(R + i, R + C + 1, v - R); // col sum is v - last
        b[i] = v;
        sum_b += v;
    }
    for (int i = 1; i <= R; i++)
        for (int j = 1; j <= C; j++) {
            g.AddEdge(i, R + j, 19);
            no[i][j] = g.edges.size() - 2; // no[i][j] is the index of arc for cell(i,j)
        }
    g.Maxflow(0, R + C + 1);


    for (int i = 1; i <= R; i++)
        for (int j = 1; j <= C; j++)
            str[i][j] = g.edges[no[i][j]].flow + 1;
    for (int i = 1; i <= R; i++) {
        int sum = 0;
        for (int j = 1; j <= C; j++) {
            sum += str[i][j];
            if (str[i][j] < 1 || str[i][j]>20) { printf("No\n"); return 0; }
        }
        if (sum != a[i]) { printf("No\n"); return 0; }
    }
    for (int j = 1; j <= C; j++) {
        int sum = 0;
        for (int i = 1; i <= R; i++)
            sum += str[i][j];
        if (sum != b[j]) { printf("No\n"); return 0; }
    }


    printf("Yes\n");
    for (int i = 1; i <= R; i++) {
        for (int j = 1; j <= C; j++)
            printf("%d ", g.edges[no[i][j]].flow + 1); // we subtracted 1 from every cell
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38995588/article/details/80620808
今日推荐