【TJOI2014】匹配(match)

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Sample Input

3
1 1 1
2 1 1
1 1 1

Sample Output

4
2 1

Data Constraint

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

分析

题目其实就是叫我们求一个带权二分图的所有的最优匹配的交集
那我们就可以先跑一遍费用流,求出一个最优匹配,然后枚举最优匹配的每一条边,将其删除之后 重新求最优匹配,若最优匹配发生变化则说明这条边在交集中。

代码

#include <bits/stdc++.h>

using namespace std;

#define maxm 20005
#define maxn 200

int me[maxn],prev[maxn],pree[maxn],inq[maxn],dis[maxn];
int s,t;
int ma[maxn],maxc1,maxc,n,gc[maxm],gcost[maxm],gv[maxm],gn[maxm],gbro[maxn],nedge;
void addedge(int u,int v,int c,int cost)
{
    gc[nedge] = c;
    gv[nedge] = v;
    gcost[nedge] = cost;
    gn[nedge] = gbro[u];
    gbro[u] = nedge;
    nedge++;
}
void mcf(int a)
{
    maxc1 = 0;
    queue<int> Q; 
    while(1)
    {
        for(int i = 1;i<=t;i++) dis[i] = INT_MAX;
        dis[s] = 0;
        Q.push(s);
        memset(inq,0,sizeof(inq));
        inq[s] = 1;
        while(!Q.empty())
        {
            int q = Q.front();
            inq[q] = 0;
            Q.pop();
            for(int i = gbro[q];i!=-1;i = gn[i])
                if(gc[i] && dis[gv[i]] > dis[q] + gcost[i] && i != a)
                {
                     prev[gv[i]] = q;
                     pree[gv[i]] = i;
                     dis[gv[i]] = dis[q] + gcost[i];
                     if(!inq[gv[i]])
                     {
                        Q.push(gv[i]);
                        inq[gv[i]] = 1;
                     }
                }
            }
        if(dis[t] == INT_MAX) break;
        maxc1 += dis[t];
        int p = t;
        while(p != s)
        {
            gc[pree[p]] = 0;
            gc[pree[p]^1] = 1;
            p = prev[p]; 
        }
    }
}                                    
int main()
{
    freopen("match.in","r",stdin);
    freopen("match.out","w",stdout);
    memset(gbro,-1,sizeof(gbro));
    scanf("%d",&n);
    for(int i = 1;i<=n;i++)
        for(int j = 1;j<=n;j++)
        {
            int t;
            scanf("%d",&t);
            addedge(i,j+n,1,-t);
            addedge(j+n,i,0,t);
        }
    s = n+n+1;
    t = n+n+2;
    for(int i = 1;i<=n;i++)
    {
        addedge(s,i,1,0);
        addedge(i,s,0,0);
        addedge(n+i,t,1,0);
        addedge(t,n+i,0,0);
    }                    
    mcf(-1);
    maxc = maxc1;
    printf("%d\n",-maxc);
    for(int i = 1;i<=n;i++)
        for(int j = gbro[i];j!=-1;j = gn[j])
            if(gc[j] == 0 && gv[j]!= s)
            {
                ma[i] = gv[j];
                me[i] = j;
                gc[j] = 1;
                gc[j^1] = 0;
                break;
            }
    for(int i = gbro[s];i!=-1;i = gn[i]) 
        gc[i] = 1,gc[i^1] = 0;
    for(int i = gbro[t];i!=-1;i = gn[i]) 
        gc[i] = 0,gc[i^1] = 1;
    for(int i = 1;i<=n;i++)
    {
        mcf(me[i]);
        if(maxc1 != maxc) printf("%d %d\n",i,ma[i]-n);
        for(int i = 1;i<=n;i++)
        for(int j = gbro[i];j!=-1;j = gn[j])
            if(gc[j] == 0 && gv[j]!= s)
            {
                gc[j] = 1;
                gc[j^1] = 0;
                break;
            }
         for(int i = gbro[s];i!=-1;i = gn[i]) 
            gc[i] = 1,gc[i^1] = 0;
         for(int i = gbro[t];i!=-1;i = gn[i]) 
            gc[i] = 0,gc[i^1] = 1;
    } 
    return 0;
}            

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值