【题解】网络流24题之分配问题

传送门

思路剖析

分配问题就是将人和工作分开来做匹配,但是与一般的二分图匹配不同的是,每个匹配都是有权值的。这个问题就是二分图最大权完美匹配问题。

图论建模

虽然这个问题可以用二分图最大权完美匹配的专门算法KM算法解决,但是这里只讲网络流解法。
首先发现人和工作的下标都是1~n的,直接建图会产生歧义,于是我们可以定义人的下标是1~n,而工作的下标是n + 1~2n。
对于第 i i 个人去做第 j j 个工作会产生的效益为 Ci,j C i , j i,jn i , j ≤ n ),相当于从 i i n+j n + j 连一条容量为1,费用为c的一条边,最后(也可以在一开始),建立源点 s s 和汇点 t t ,从 s s 向下标为1 ~ n的所有结点都连一条容量为1,费用为0的边,类似地,我们从下标为n + 1 ~ 2n的所有结点都向 t t 连一条容量为1,费用为0的边接着从 s s t t 跑一遍最小费用最大流即可,所求得的最小费用就是第一问的答案,对于第二问求最大收益该怎么解决呢?其实也不难想,重构一遍图,将容量设为原来容量的相反数,再跑一遍最小费用最大流,将所求的的最小费用取个相反数就是第二问的答案。

code c o d e

#include
#include
#include
#include
#include
#include
#define min(x, y) ((x) < (y) ? (x) : (y))
int a[101][101];
struct Graph {
    static const int infty = 0x7f7f7f7f;
    struct edgetype {
        int to, next, flow, cap, cost;
    };
    int n, m, s, t;
    std::vector< edgetype > edge;
    std::vector< bool > inQ;
    std::vector< int > head, dist, path;
    std::queue< int > Q;
    Graph() {
        n = m = 0;
        head.clear(); dist.clear(); edge.clear(); inQ.clear(); path.clear(); 
    }
    Graph(int n, int m) {
        this->n = n; this->m = m;
        head.resize(n + 1); dist.resize(n + 1); inQ.resize(n + 1); path.resize(n + 1); 
        head.assign(n + 1, -1);
    }
    void AddEdge(int from, int to, int cap, int cost) {
        edge.push_back((edgetype){to, head[from], 0, cap, cost});
        head[from] = edge.size() - 1;
        edge.push_back((edgetype){from, head[to], 0, 0, -cost});
        head[to] = edge.size() - 1;
    }
    bool FindPath() {
        dist.assign(n + 1, infty);
        inQ.assign(n + 1, false);
        path.assign(n + 1, -1);
        while (!Q.empty()) Q.pop();
        Q.push(s); dist[s] = 0; inQ[s] = true; 
        while (!Q.empty()) {
            int u = Q.front(); Q.pop(); inQ[u] = false;
            for (int i = head[u]; i != -1; i = edge[i].next) {
                int v = edge[i].to;
                if (edge[i].flow < edge[i].cap && dist[v] > dist[u] + edge[i].cost) {
                    dist[v] = dist[u] + edge[i].cost; path[v] = i; 
                    if (!inQ[v]) {
                        Q.push(v);
                        inQ[v] = true;
                    }
                }
            }
        }
        return path[t] != -1;
    }
    void Argument(long long& Mincost) {
        long long alpha = infty;
        for (int i = path[t]; i != -1; i = path[edge[i ^ 1].to]) 
            alpha = min(alpha, edge[i].cap - edge[i].flow);
        Mincost += dist[t] * alpha;
        for (int i = path[t]; i != -1; i = path[edge[i ^ 1].to]) {
            edge[i].flow += alpha;
            edge[i ^ 1].flow -= alpha;
        }
    }
    long long MincostMaxflow(int s, int t) {
        this->s = s; this->t = t;
        long long Mincost = 0;
        while (FindPath())
            Argument(Mincost);
        return Mincost;
    }
};
Graph G;

int main() {
    int n;
    scanf("%d", &n);
    G = Graph(2 * n + 2, n * n + 2 * n);
    for (int i = 1; i <= n; i++) {
        G.AddEdge(0, i, 1, 0);
        G.AddEdge(n + i, n * 2 + 1, 1, 0);
    }
    for (int i = 1; i <= n; i++) 
        for (int j = 1; j <= n; j++) {
            scanf("%d", &a[i][j]);
            G.AddEdge(i, n + j, 1, a[i][j]);
        }
    printf("%lld\n", G.MincostMaxflow(0, 2 * n + 1));
    G = Graph(2 * n + 2, n * n + 2 * n);;
    for (int i = 1; i <= n; i++) {
        G.AddEdge(0, i, 1, 0);
        G.AddEdge(n + i, n * 2 + 1, 1, 0);
    }
    for (int i = 1; i <= n; i++) 
        for (int j = 1; j <= n; j++) 
            G.AddEdge(i, n + j, 1, -a[i][j]);
    printf("%lld\n", -G.MincostMaxflow(0, 2 * n + 1));
    return 0;
}

你可能感兴趣的:(图论,网络流24题,网络流,二分图)