[20200721省选B模拟T3]匹配

题目链接:

  P3967 [TJOI2014]匹配

 

题目大意:

  给定n男n女,n男与n女两两之间有一个幸福值H[i][j],请求出他们之间的完美匹配并且输出匹配的必须边的端点.($n\leq80$)

solution:

   对于完美匹配,我们可以跑最大费用最大流.至于求必须边,不难发现第一次跑完完美匹配后,男女点之间只流过了n条边,所以剩余的边肯定不是必须边.由于n值较小,所以我们可以一一枚举第一次费用流所流过的边,断边观察最大费用是否改变,若改变则统计答案即可.

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define R register
#define next exnttttttttttttttttttttt
#define debug puts("mlg")
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
inline ll read();
inline void write(ll x);
inline void writesp(ll x);
inline void writeln(ll x);
ll n,S,T;
ll H[88][88];
ll to[50000],from[50000],head[50000],next[50000],tot=1,c[50000],w[50000];
inline void add(ll x,ll y,ll u,ll v){
    to[++tot]=y;from[tot]=x;next[tot]=head[x];head[x]=tot;c[tot]=u;w[tot]=v;
}
ll d[50000],last[50000],incf[50000],pre[50000];
bool vis[50000];
queue<ll>q;
inline bool spfa(){
    for(R ll i=1;i<=T;i++) d[i]=-1000000;
    memset(vis,0,sizeof vis);
    while(q.size())q.pop();
    incf[S]=12534567984654089;
    d[S]=0;
    q.push(S);pre[T]=-1;
    while(!q.empty()){
        ll x=q.front();q.pop();vis[x]=false;
        for(R ll i=head[x],ver;i;i=next[i]){
            ver=to[i];
            if(!c[i]) continue;
            if(d[ver]<d[x]+w[i]){
                d[ver]=d[x]+w[i];
                incf[ver]=min(incf[x],c[i]);
                last[ver]=i;
                pre[ver]=x;
                if(!vis[ver]){
                    vis[ver]=true;
                    q.push(ver);
                }
            }
        }
    }
    return d[T]!=-1000000;
}
ll ans,maxflow;
inline void update(){
    ll x=T;
    while(x!=S){
        c[last[x]]-=incf[T];
        c[last[x]^1]+=incf[T];
        x=pre[x];
    }
    ans+=d[T]*incf[T];
    maxflow+=incf[T];
}
ll TOT,D[5000];
bool check[90][90];
int main(){
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    n=read();
    for(R ll i=1;i<=n;i++){
        for(R ll j=1;j<=n;j++){
            H[i][j]=read();
        }
    }
    S=n*2+1,T=S+1;
    for(R ll i=1;i<=n;i++){
        add(S,i,1,0);add(i,S,0,0);
        add(i+n,T,1,0);add(T,i+n,0,0);
    }
    for(R ll i=1;i<=n;i++){
        for(R ll j=1;j<=n;j++){
            add(i,j+n,1,H[i][j]);
            add(j+n,i,0,-H[i][j]);
        }
    }
    while(spfa()) update();
    writeln(ans);
    ll Ans=ans;
    for(R ll i=2;i<tot;i+=2){
        if(!c[i]&&from[i]!=S&&to[i]!=T) D[++TOT]=i;
        c[i]+=c[i^1];c[i^1]=0;
    }
    for(;TOT;--TOT){
        for(R ll i=2;i<tot;i+=2) c[i]+=c[i^1],c[i^1]=0;
        ll C=c[D[TOT]];c[D[TOT]]=0,ans=0;
        while(spfa()) update();
        if(Ans!=ans){
            check[from[D[TOT]]][to[D[TOT]]-n]=true;
        }
        c[D[TOT]]+=C;
    }
    for(R ll i=1;i<=n;i++){
        for(R ll j=1;j<=n;j++) if(check[i][j]) writesp(i),writeln(j);
    }
}
inline ll read(){
    ll x=0,t=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') t=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*t;
}
inline void write(ll x){
    if(x<0){putchar('-');x=-x;}
    if(x<=9){putchar(x+'0');return;}
    write(x/10);putchar(x%10+'0');
}
inline void writesp(ll x){
    write(x);putchar(' ');
}
inline void writeln(ll x){
    write(x);putchar('\n');
}

 

posted @ 2020-07-21 16:16  月落乌啼算钱  阅读(164)  评论(0)    收藏  举报