cf 546 E. Soldier and Traveling - 网络流

传送门
给出每一个城市的初始人数,以及期望人数,每个人最多只能走一步。最后告诉城市的连接情况。
如果可以,就输出yes,在输出\(n^2\)的矩阵,\(i = j\)时,输出第\(i\)个城市需要留几个人。否则输出\(i\)城市到\(j\)城市需要走几个人。

建立一个超级源点和一个超级汇点,
超级源点连接所有的城市,边权是\(a_i\),对于每一个城市再构建虚拟城市,与超级汇点连接,边权是\(b_i\)
再把每一个城市和它的虚拟城市连接,边权是无穷大。
再按照题目给的连接情况进行建边,边权是无穷大。

然后跑最大流即可
关于匹配情况,可以通过他的反向边来看,因为匹配时其实就3个点,那么反向边就是这个城市到那个城市的人数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
using namespace std;
const int N = 200 + 5;
const int M = 5e3 + 5;
const int inf = 0x3f3f3f3f;
template<typename T = long long> inline T read() {
    T s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
    while(isdigit(ch)) {s = (s << 3) + (s << 1) + ch - 48; ch = getchar();} 
    return s * f;
}
namespace MAX_FLOW {
    const int N = 200 + 5;
    int st, ed;
    struct Edge{
        int from, to, next, w;
    }e[M << 1];
    int head[N], tot = 1, cur[N]; //弧优化
    void add(int u, int v, int w){
        e[++tot].to = v;
        e[tot].from = u;
        e[tot].w = w;
        e[tot].next = head[u];
        head[u] = tot;
    }
    void add_edge(int u, int v, int w){
        add(u, v, w), add(v, u, 0);
    }
    int dis[N];
    int dfs(int u, int flow){ //保证了每次DFS都能找到增广路
        if(u == ed) return flow;
        int sum = 0;
        for(int i = cur[u]; i && flow; i = e[i].next){
            cur[u] = i;
            int v = e[i].to, w = e[i].w;
            if(w > 0 && dis[v] == dis[u] + 1){
                int t = dfs(v, min(flow, w)); //获取这条增广路的最小流量
                e[i].w -= t; e[i ^ 1].w += t; //减去最小流量,同时反向边加上最小流量
                flow -= t; sum += t;
            }
        }
        if(!sum) dis[u] = 0;//结果u无法到达终点,或者没有增广路,切断经过这个点的路径
        return sum;
    }
    bool bfs(){//分层判断是否有增广路
        memset(dis, 0, sizeof(dis));
        queue<int> q;
        q.push(st); dis[st] = 1; cur[st] = head[st];//弧优化
        while(!q.empty()){
            int u = q.front(); q.pop();
            for(int i = head[u]; i; i = e[i].next){
                int v = e[i].to, w = e[i].w;
                if(w > 0 && !dis[v]) {
                    cur[v] = head[v];// v这个点从head[v]出发是可行的
                    dis[v] = dis[u] + 1;//分层
                    q.push(v);
                    if(v == ed) return 1;//已经到达增广路,直接返回
                }
            }
        }
        return dis[ed];
    }
    ll Dinic(){
        ll max_flow = 0;
        while(bfs())
            max_flow += dfs(st, inf);
        return max_flow;
    }
};
int a[N], b[N];
int ans[N][N];
int main(){
    int n = read(), m = read(); int s = 0, t = 2 * n + 1;
    MAX_FLOW::st = s, MAX_FLOW::ed = t;
    int sum1 = 0, sum2 = 0;
    for(int i = 1; i <= n; i++) a[i] = read(), MAX_FLOW::add_edge(s, i, a[i]), sum1 += a[i];
    for(int i = 1; i <= n; i++) b[i] = read(), MAX_FLOW::add_edge(i + n, t, b[i]), sum2 += b[i];
    for(int i = 1; i <= n; i++) MAX_FLOW::add_edge(i, i + n, inf);
    for(int i = 1; i <= m; i++) {
        int u = read(), v = read();
        MAX_FLOW::add_edge(u, v + n, inf);
        MAX_FLOW::add_edge(v, u + n, inf);
    }
    if(sum1 != sum2) {
        printf("NO\n"); return 0;
    }
    ll max_flow = MAX_FLOW::Dinic();
    if(sum2 != max_flow) {
        printf("NO\n"); return 0;
    }
    for(int i = 4 * n + 2; i <= MAX_FLOW::tot; i += 2) {
        if(MAX_FLOW::e[i ^ 1].w) ans[MAX_FLOW::e[i].from][MAX_FLOW::e[i].to - n] = inf - MAX_FLOW::e[i].w; 
    }
    printf("YES\n");
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            printf("%d%c", ans[i][j], " \n"[j == n]);
    return 0; 
}
posted @ 2021-01-30 16:01  Emcikem  阅读(58)  评论(0编辑  收藏  举报