2018-2019 ICPC, NEERC, Southern Subregional Contest I. Privatization of Roads in Berland 最大流

100 篇文章 0 订阅

题目链接: https://codeforces.com/contest/1070/problem/I

题意:

给你 n n n 个城市 m m m 条路径,你现在要把这些路径分给一些公司(公司数量可视作无限大)使得: ① 每个公司最多有两条路径 ② 每个城市附近的路径最多只能属于 k k k 个不同的公司。 要你给出一种每条路径所属公司的可行解(若无解则全部输出0)。

做法:

不愧是2600的题目。。就算有点和边都只有600个这样的提示,也很难想象到网络流应该怎么做。

想了很久最后还是投降后学的做法。 因为每个城市附近的路径最多只能属于 k k k 个公司,所以如果一个城市 x x x 附近的路径数量 d e [ x ] > k de[x]>k de[x]>k ,那么这个城市附近的路径中就有 2 ∗ ( d e [ x ] − k ) 2*(de[x]-k) 2(de[x]k) 条路径需要被合并。

怎么体现大家都 “被合并” 这个概念呢,将所有的路径和汇点连一条边流量为 1 1 1 ,表示一条路径只能被合并一起(即和其他一条路径有同样的值)。城市向附近对应的路径连边流量为 1 1 1,如果这个城市有需要被合并的路径即 d e [ x ] > k de[x]>k de[x]>k,那么源点向它连边流量为 2 ∗ ( d e [ x ] − k ) 2*(de[x]-k) 2(de[x]k)

跑出最大流后如果和需要连边的数值是一样的那么就是存在可行解的,对于所有的点,如果它连向的边是合法的,那么就加入到集合中一个个标起来就好乐。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
#define mid (l+r)/2
using namespace std;
const int maxn=1205;
const int maxm=5005;
const int MAX = 1<<26;
typedef long long ll;
typedef pair<int,int> pii;
int nex[maxm],de[maxn],to[maxm],cap[maxm],from[maxm];
int n,m,k,cnt,head[maxn],d[maxn],sp,tp,np,col[maxm];//原点,汇点
vector<int> ans;
//理论复杂度n2*m
void add(int u,int v,int c){
    //printf("from = %d  to = %d cap = %d\n",u,v,c);
    from[cnt]=u,to[cnt]=v,cap[cnt]=c,nex[cnt]=head[u],head[u]=cnt++;
    from[cnt]=v,to[cnt]=u,cap[cnt]=0,nex[cnt]=head[v],head[v]=cnt++;
}
int bfs(){
    queue <int> q;
    memset(d,-1,sizeof(d));
    d[sp]=0;
    q.push(sp);
    while(!q.empty()){
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=nex[i]){
            int u=to[i];
            if(d[u]==-1 && cap[i]>0){
                d[u]=d[cur]+1;
                q.push(u);
            }
        }
    }
    return d[tp] != -1;
}
int dfs(int a,int b){
    int r=0;
    if(a==tp)return b;
    for(int i=head[a];i!=-1 && r<b;i=nex[i])
    {
        int u=to[i];
        if(cap[i]>0 && d[u]==d[a]+1)
        {
            int x=min(cap[i],b-r);
            x=dfs(u,x);
            r+=x;
            //printf("from  = %d to = %d edge = %d sub = %d\n",a,u,i,x);
            cap[i]-=x;
            cap[i^1]+=x;
        }
    }
    if(!r)d[a]=-2;
    return r;
}
 
int dinic(int sp,int tp){
    int total=0,t;
    while(bfs()){
        while(t=dfs(sp,MAX))
        total+=t;
    }
    return total;
}
void init(){
    rep(i,0,n+m+1) head[i]=-1,de[i]=0;
    rep(i,1,m) col[i]=0;
    cnt=0; np=0;
}
int main(){
    int T; scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        init();
        sp=0,tp=n+m+1;
        rep(i,1,m){
            int x,y; scanf("%d%d",&x,&y);
            de[x]++,de[y]++;
            add(x,n+i,1); add(y,n+i,1); add(n+i,tp,1);
        }
        int flag=0,sum=0;
        rep(i,1,n){
            if(de[i]>2*k) flag=1;
            if(de[i]>k) add(sp,i,2*(de[i]-k)),sum+=2*(de[i]-k);
        }
        if(flag||sum!=dinic(sp,tp)){
            rep(i,1,m){
                printf("0%c",i==m?'\n':' ');
            }
        }
        else{
            for(int u=1;u<=n;u++){
                for(int i=head[u];~i;i=nex[i]){
                    if(to[i]<=m+n&&to[i]>n&&cap[i]==0) {
                        ans.push_back(to[i]);
                    }
                }
            }
            for(int i=0;i<(int)ans.size();i+=2){
                int x=ans[i]-n,y=ans[i+1]-n;
                col[x]=col[y]=++np;
            }
            rep(i,1,m) {
                if(col[i]==0) col[i]=++np;
                printf("%d%c",col[i],i==m?'\n':' ');
            }
            ans.clear();
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值