codeforces 546E Soldier and Traveling 网络流

题目传送门

题目大意:有n座城市,每座城市都有一些初始的士兵$Ai$,每个士兵只能通过一条路径达到相邻的城市,然后问能否使得第i座城市的士兵数量是$Bi$,如果可以,要输出流动的方案。

思路:如果不需要输出流动的方案,那么就是个简单的网络流模板题了,而考虑上输出方案,则需要把一个点拆成$i$和$n+i$,建立$(S,i,a[i]),(i,i+n,inf),(i+n,T,b[i])$三条边,对于一条路径(u,v),则建立$(u,v+n,inf),(v,u+n,inf)$两条边,然后跑最大流,判断最后的流和和b数组的和是否相等则可以得到合法性。

  对于输出路径,则要遍历u的每一条边,如果终点v大于n,则将这条边的反向边流量带入答案。这里就体现了拆点的意义,能将反向边和初始的边区别开来。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 10;

struct Edge {
    int to, flow, nxt;
    Edge() {}
    Edge(int to, int nxt, int flow):to(to),nxt(nxt), flow(flow) {}
} edge[maxn << 2];

int head[maxn], dep[maxn];
int S, T;
int N, n, m, tot;
int mp[110][110];
void init(int n) {
    N = n;
    for (int i = 0; i <= N; ++i) head[i] = -1;
    tot = 0;
}

void addv(int u, int v, int w, int rw = 0) {
    edge[tot] = Edge(v, head[u], w);
    head[u] = tot++;
    edge[tot] = Edge(u, head[v], rw);
    head[v] = tot++;
}

bool BFS() {
    for (int i = 0; i < N; ++i) dep[i] = -1;
    queue<int>q;
    q.push(S);
    dep[S] = 1;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; ~i; i = edge[i].nxt) {
            if (edge[i].flow && dep[edge[i].to] == -1) {
                dep[edge[i].to] = dep[u] + 1;
                q.push(edge[i].to);
            }
        }
    }
    return dep[T] < 0 ? 0 : 1;
}

int DFS(int u, int f) {
    if (u == T || f == 0) return f;
    int w, used = 0;
    for (int i = head[u]; ~i; i = edge[i].nxt) {
        if (edge[i].flow && dep[edge[i].to] == dep[u] + 1) {
            w = DFS(edge[i].to, min(f - used, edge[i].flow));
            edge[i].flow -= w;
            edge[i ^ 1].flow += w;
            used += w;
            if (used == f) return f;
        }
    }
    if (!used) dep[u] = -1;
    return used;
}

int Dicnic() {
    int ans = 0;
    while (BFS()) {
        ans += DFS(S, INF);
    }
    return ans;
}


int main() {
    while(cin>>n>>m)
    {
        init(2*n+2);
        S=0,T=2*n+1;
        int sum=0,ans=0;
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            addv(S,i,x);
            addv(i,i+n,INF);
            sum+=x;
        }
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            addv(i+n,T,x);
            ans+=x;
        }
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addv(u,v+n,INF);
            addv(v,u+n,INF);
        }
        if(ans!=sum){
            printf("NO\n");
            continue;
        }
        ans=Dicnic();
        if(ans!=sum){
            printf("NO\n");
            continue;
        }else puts("YES");
        for(int u=1;u<=n;u++)
        {
            for(int i=head[u];i!=-1;i=edge[i].nxt){
                int v=edge[i].to;
                if(v>n){
                    mp[u][v-n]=edge[i^1].flow;
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                printf("%d%c",mp[i][j]," \n"[j==n]);
            }
        }
    }
}

 

posted @ 2019-04-24 13:19  光芒万丈小太阳  阅读(227)  评论(0编辑  收藏  举报