【TJOI2014】匹配

Description

这里写图片描述

对于100%的数据,N<=80

Analysis

这个第一问是二分图最大带权匹配
可以用KM算法(并不会)和 费用流去做
对于第二问,第一问求出任意一个最大带权匹配的时候记录一下匹配的边
枚举这些边,删掉,再跑一遍,如果答案改变就说明边在交集里

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=170,C=100000000,INF=2139062144;
int n,S,T,ans,a[N][N],r[N][N],c[N][N],dis[N],b[N];
bool bz[N];
void link(int u,int v,int ro,int co)
{
    a[u][++a[u][0]]=v,r[u][v]=ro,c[u][v]=co;
    if(ro) link(v,u,0,-co);
}
int aug(int v,int flow)
{
    bz[v]=1;
    if(v==T)
    {
        ans+=flow*dis[S];
        return flow;
    }
    fo(i,1,a[v][0])
    {
        int u=a[v][i];
        if(r[v][u] && !bz[u] && dis[v]==dis[u]+c[v][u])
        {
            int f=aug(u,min(r[v][u],flow));
            if(f)
            {
                r[v][u]-=f,r[u][v]+=f;
                return f;
            }
        }
    }
    return 0;
}
bool change()
{
    int minh=INF;
    fo(i,S,T)
        if(bz[i])
            fo(j,1,a[i][0])
            {
                int u=a[i][j];
                if(r[i][u] && !bz[u]) minh=min(minh,dis[u]+c[i][u]-dis[i]);
            }
    if(minh==INF) return 0;
    fo(i,S,T)
        if(bz[i]) dis[i]+=minh,bz[i]=0;
    return 1;
}
int main()
{
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    int x;
    scanf("%d",&n);
    S=0,T=n+n+1;
    fo(i,1,n)
        fo(j,1,n)
        {
            scanf("%d",&x);
            link(i,n+j,1,C-x);
        }
    fo(i,1,n) link(S,i,1,0),link(n+i,T,1,0);
    do
    {
        while(aug(S,INF)) memset(bz,0,sizeof(bz));
    }
    while(change());
    fo(i,1,n)
        fo(j,n+1,n+n)
            if(!r[i][j]) b[i]=j;
    int t=ans;
    printf("%d\n",C*n-t);
    fo(k,1,n)
    {
        fo(i,1,n) r[S][i]=1,r[i][S]=0;
        fo(i,1,n)
            fo(j,n+1,n+n) r[i][j]=1,r[j][i]=0;
        fo(i,n+1,n+n) r[i][T]=1,r[T][i]=0;
        r[k][b[k]]=r[b[k]][k]=0;
        ans=0;
        memset(dis,0,sizeof(dis));
        do
        {
            while(aug(S,INF)) memset(bz,0,sizeof(bz));
        }
        while(change());
        if(t!=ans) printf("%d %d\n",k,b[k]-n);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值