八省联考2018 劈配(网络流)

碰到这种匹配的题

二分图匹配,网络流嘛

需要将选手按照排名从大倒小依次匹配,每个选手都是在上一个选手的残留网络上继续匹配

为了满足当前选手的最优匹配,需要从小到大按顺序枚举选手的志愿进行匹配

这样暴力建图匹配复杂度有点高,所以考虑二分,发现前i个志愿中有可以被录取的的,那更多的志愿一定可以被录取,是满足二分性的

这样可以找到一个人的最优志愿匹配,第一问ok

对于第二问,我们发现当一个人在一个排名可以满足期望值的时候,排名更靠前时一定更能满足

那么就可以二分,求出满足期望值要增加最少的排名

两次二分的建图方法是这样的

源点向学员连容量为1的边,导师向汇点连容量为战队人数限制容量的边,学员和其选择的导师间连容量为1的边

跑dinic,如果有流量,就表示能够匹配到

首先将这个人之前(第一问就是编号这个排名,第二问要用编号排名减去上升的名次)的人的最优录取志愿中的导师加到图当中,这样保证之前的人匹配到的志愿依旧是满足前几个人的最优匹配的

因为每个志愿选择的导师不会超过C个(C<=10),可以保证建图的复杂度

(这个地方刚开始我是将所有志愿全都加进去,后来发现是不对的,并不能保证是最优的),然后跑一遍dinic,在残余网络上进行这个人的匹配

这个人进行匹配时,第一问要将二分的志愿之前的所有志愿加到图中,第二问要将这个人最低期望录取志愿之前的所有志愿加到图中,跑一遍dinic

可以发现,这样一共会跑O(nlogn)次dinic,时间复杂度是可以接受的

注意点:我是用二维的vector存储每个人每个志愿想要选择的导师的,每组数据之前一定要保证完全清空(每组数据的最后将vector清空),否则会出问题的,因为这个debug了好久

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;

const int maxn = 2010;
const int INF = 1e9+7;

int T,C,n,m,s,t;
int b[maxn],a[maxn][maxn],S[maxn];
int res1[maxn],res2[maxn];

vector<int> v[maxn][maxn];

int h[maxn],size=1;
struct E{
    int to,next,cap;
}e[maxn<<10];
void add(int u,int v,int c){
    e[++size].to=v;
    e[size].next=h[u];
    e[size].cap=c;
    h[u]=size;
}

int d[maxn];
queue<int> q;
int bfs(){
    memset(d,0,sizeof(d));
    while(!q.empty()) q.pop();
    d[s]=1; q.push(s);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=h[u];i!=-1;i=e[i].next){
            int v=e[i].to;
            if(!d[v]&&e[i].cap){
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return d[t];
}

int dfs(int u,int lim){
    if(u==t) return lim;
    int f=0,tmp;
    for(int i=h[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(d[v]==d[u]+1&&e[i].cap&&(tmp=dfs(v,min(lim,e[i].cap)))){
            e[i].cap-=tmp,e[i^1].cap+=tmp;
            f+=tmp,lim-=tmp;
            if(!lim) return f;
        }
    }
    if(lim) d[u]=0;
    return f;
}

int dinic(){
    int ans=0;
    while(bfs()){
        ans+=dfs(s,INF);
    }
    return ans;
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;}

int main(){
    T=read(),C=read();
    while(T--){
        memset(h,-1,sizeof(h));
        n=read(),m=read();
        s=0,t=n+m+1;
        for(int i=1;i<=m;i++) b[i]=read();
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                a[i][j]=read();
                v[i][a[i][j]].push_back(j);
            }
        }
        
        for(int i=1;i<=n;i++) S[i]=read();
        
        for(int i=1;i<=m;i++){
            add(i+n,t,b[i]),add(t,i+n,0);
        }
        
        for(int i=1;i<=n;i++){
            int l=1,r=m; int res=m+1;
            while(l<=r){
                int mid=(l+r)/2;
                memset(h,-1,sizeof(h)); size=1;
                for(int j=1;j<i;j++) add(s,j,1),add(j,s,0); 
                for(int j=1;j<i;j++){ 
                    int now=res1[j]; 
                    for(int k=0;k<v[j][now].size();k++){
                        add(j,v[j][now][k]+n,1),add(v[j][now][k]+n,j,0);
                    }
                }
                for(int j=1;j<=m;j++){ add(j+n,t,b[j]),add(t,j+n,0); }
            
                dinic();
                
                add(s,i,1),add(i,s,0);
                for(int j=1;j<=mid;j++){
                    for(int k=0;k<v[i][j].size();k++){
                        add(i,v[i][j][k]+n,1),add(v[i][j][k]+n,i,1);
                    }
                }
                
                if(dinic()){
                    res=mid;
                    r=mid-1;
                }else{
                    l=mid+1;
                }
            }
            res1[i]=res;
        }
        
        for(int i=1;i<=n;i++) printf("%d ",res1[i]); printf("\n");
        
        for(int i=1;i<=n;i++){
            if(res1[i]>S[i]){
                int l=1,r=i-1; int res=i;
                while(l<=r){
                    int mid=(l+r)/2;
                    memset(h,-1,sizeof(h)); size=1;
                    
                    for(int j=1;j<i-mid;j++) add(s,j,1),add(j,s,0);
                    for(int p=1;p<i-mid;p++){
                        int now=res1[p];
                        for(int k=0;k<v[p][now].size();k++){
                            add(p,v[p][now][k]+n,1),add(v[p][now][k]+n,p,0);
                        }
                    }
                    for(int j=1;j<=m;j++){ add(j+n,t,b[j]),add(t,j+n,0); }
                    dinic();

                    add(s,i,1),add(i,s,0);
                    for(int j=1;j<=S[i];j++){
                        for(int k=0;k<v[i][j].size();k++){
                            add(i,v[i][j][k]+n,1),add(v[i][j][k]+n,i,0);
                        }
                    }
                    
                    if(dinic()){
                        res=mid;
                        r=mid-1;
                    }else{
                        l=mid+1;
                    }
                }
                res2[i]=res;
            }else{
                res2[i]=0;
            }
        }
        for(int i=1;i<=n;i++) printf("%d ",res2[i]); printf("\n");
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                v[i][j].clear();
            }
        }
    }

    return 0;
}
原文地址:https://www.cnblogs.com/tuchen/p/10567124.html