这道题肯定用网络流,不然给你放在网络流考试里干嘛

题意:

给了一个有向无环图,给(除了根节点)每个节点选一条出边构成一棵树,让儿子个数最多的节点的儿子个数最少。(根节点不算
依次输出每个节点的父亲,要求字典序最小


先不考虑字典序,考虑计算最小的负载级别。

很显然想到负载级别可以用二分答案来求。

判断负载级别为K是否可行,于是我们可以建出这样一幅图来:

然后计算最大流,如果最大流=,说明负载级别可以等于


那么如何验证字典序最小呢?

枚举

在过程中,我们已经确定了,此时枚举未确定。

那么节点只和已确定的父亲连边,号节点也和枚举的父亲连边,按原来的方式连边(可能有多条)。

如果当前建图跑最大流=n,说明当前可行,就定下来枚举

如果最大流,说明不行,继续枚举的出边作为,继续验证即可

要跑次网络流,可以过,但是觉得他不够优秀。

优化(跑次最大流)

刚才,枚举的出边作为

现在:网络流验证 是否可行

于是号节点就和~连边

就可以二分验证

一开始已知在有解

第一轮就验证

第二轮...

其实也快不到那里去...

#include<bits/stdc++.h>
using namespace std;
const int N=205;
const int M=5005;
const int inf=1e9;
int n,k,S,T,fa[N];
char a[N][N];
int Last[N],Next[M],End[M],len[M],tot;
int _last[N],dis[N],gap[N];
void cb(int x,int y,int z,int z0=0){
    End[tot]=y,Next[tot]=Last[x],len[tot]=z,Last[x]=tot++;
    End[tot]=x,Next[tot]=Last[y],len[tot]=z0,Last[y]=tot++;
}
void bfs(){
    for(int i=1;i<=T;i++) dis[i]=T,gap[i]=0;
    gap[0]=0;
    dis[T]=0;
    queue<int> q;
    q.push(T);
    while(q.size()){
        int x=q.front();
        q.pop();
        for(int i=Last[x];i;i=Next[i]){
            int y=End[i];
            if(dis[y]>dis[x]+1){
                dis[y]=dis[x]+1;
                q.push(y);
            }
        }
    }
    for(int i=1;i<=T;i++) gap[dis[i]]++,_last[i]=Last[i];
}
int isap(int x,int flow){
    if(x==T) return flow;
    int flow_now=0;
    for(int &i=_last[x];i;i=Next[i]){
        int y=End[i];
        if(len[i] && dis[x]==dis[y]+1){
            int f=isap(y,min(len[i],flow-flow_now));
            flow_now+=f;
            len[i]-=f;
            len[i^1]+=f;
            if(flow==flow_now || dis[S]==T) return flow_now;
        }
    }
    gap[dis[x]]--;
    if(gap[dis[x]]==0) dis[S]=T;
    dis[x]++;
    gap[dis[x]]++;
    _last[x]=Last[x];
    return flow_now;
}
bool check(int mid){
    S=2*n+1;
    T=S+2;
    tot=2;
    for(int i=1;i<=n;i++){
        cb(S,i,1);
        if(fa[i]) cb(i,fa[i]>n?T:fa[i]+n,1);
        else if(a[0][i]=='Y') cb(i,T,1);
        else
            for(int j=1;j<=n;j++)
                if(a[i][j]=='Y')
                    cb(i,j+n,1);
        cb(i+n,T,mid);
    }
    int res=0;
    bfs();
    while(dis[S]<T) res+=isap(S,inf);
    for(int i=0;i<=T;i++) Last[i]=0;
    if(res==n) return 1;
    return 0;
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<=n;i++) scanf("%s",a[i]+1);
    for(int i=1;i<=n;i++) a[i][n+1]=a[0][i];
    int l=0,r=n;
    //------
    while(l<=r){
        int mid=l+r>>1;
        if(check(mid)){
            r=mid-1;
            k=mid;
        }
        else l=mid+1;
    }
    //------
    for(int i=1;i<=n;i++){
        bool flag=0;
        for(int j=1;j<=n;j++){
            if(a[i][j]=='Y'){
                fa[i]=j;
                if(check(k)){
                    flag=1;
                    break;
                }
            }
        }
        if(flag) continue;
        fa[i]=n+1;//fa[i]=CC
    }
    //------
    for(int i=1;i<=n;i++) printf("%d ",fa[i]-1);
    return 0;
}