Codeforces546 E. Soldier and Traveling(网络流+输出方案)

题意:

在这里插入图片描述

解法:
首先,如果sum{a[i]}!=sum{b[i]},那么一定无解,这里先特判一下.

容易看出网络流可以判断是否有解.

设源点S,汇点T,
将点i上的人不动,看作是存在一条(i,i)的边.
将每个点拆为出点和入点,令id1[i]表示点i的出点,id2[i]表示点i的入点.

建图:
(S->id1[i],a[i]),即每个点有a[i]个人.
(id2[i]->T,b[i]),即每个点需要b[i]个人.
(id1[i]->id2[i],inf),即点i的人可以留在原地.
如果存在边(a,b),那么(id1[a]->id2[b],inf),(id1[b]->id2[a],inf),即a和b的人可以互相走.

跑dinic求最大流,如果maxflow!=sum{B[i]},那么无解,否则有解.

这题还需要输出一组方案,
我们到残余网络上的回边中找流的大小即可,回边的流量就是走这条边的人数.
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=2e6+5;
int head[maxm],nt[maxm<<2],to[maxm<<2],w[maxm<<2],cnt=1;
int d[maxm],S,T,maxflow;
//
int a[111],b[111];
int ans[111][111];
int id1[111],id2[111],rev[maxm],idx;
int n,m;
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void add2(int x,int y,int z){
    add(x,y,z);add(y,x,0);
}
bool bfs(){
    for(int i=1;i<=idx;i++)d[i]=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=nt[i]){
            int v=to[i];
            if(!d[v]&&w[i]){
                d[v]=d[x]+1;
                q.push(v);
                if(v==T)return 1;
            }
        }
    }
    return 0;
}
int dfs(int x,int flow){
    if(x==T)return flow;
    int res=flow;
    for(int i=head[x];i;i=nt[i]){
        int v=to[i];
        if(w[i]&&d[v]==d[x]+1){
            int k=dfs(v,min(res,w[i]));
            w[i]-=k;
            w[i^1]+=k;
            res-=k;
            if(!k)d[v]=-1;
            if(!res)break;
        }
    }
    return flow-res;
}
void dinic(){
    maxflow=0;
    while(bfs()){
        maxflow+=dfs(S,1e9);
    }
}
void solve(){
    cin>>n>>m;
    int sumA=0,sumB=0;
    for(int i=1;i<=n;i++)cin>>a[i],sumA+=a[i];
    for(int i=1;i<=n;i++)cin>>b[i],sumB+=b[i];
    if(sumA!=sumB){
        cout<<"NO"<<endl;return ;
    }
    for(int i=1;i<=n;i++){
        id1[i]=++idx;rev[idx]=i;
        id2[i]=++idx;rev[idx]=i;
    }
    S=++idx;T=++idx;
    for(int i=1;i<=n;i++){
        add2(S,id1[i],a[i]);
        add2(id2[i],T,b[i]);
    }
    for(int i=1;i<=n;i++){
        add2(id1[i],id2[i],1e9);
    }
    for(int i=1;i<=m;i++){
        int x,y;cin>>x>>y;
        add2(id1[x],id2[y],1e9);
        add2(id1[y],id2[x],1e9);
    }
    dinic();
    if(maxflow!=sumB){
        cout<<"NO"<<endl;return ;
    }
    cout<<"YES"<<endl;
    for(int k=1;k<=n;k++){
        int x=id1[k];
        for(int i=head[x];i;i=nt[i]){
            if(i&1)continue;
            int v=to[i];
            if(v==S||v==T)continue;
            v=rev[v];
            ans[k][v]+=w[i^1];//w[i^1]是回边的流量
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cout<<ans[i][j]<<' ';
        }
        cout<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);
    solve();
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值