HDU 2853 (KM最大匹配)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2853

题目大意:二分图匹配费用流。①最大匹配②最小原配变动

解题思路

如果去掉第二个要求,那么就是裸KM。

然而加上第二个要求,那么就需要一种新的建图方式。

建图

对于输入矩阵,每一条边,cost扩大K倍($K=n+1$)

对于原配,每一条边cost在扩大K倍基础上+1

KM

统计cost时,直接把cost整除K,然后累加。

并且Hash一下原配边的变动情况。

扩大K倍的作用

准确来说,K倍是为了+1而存在的。由于要优先考虑原配边,所以cost要大些。

但是,加大的cost又要使得最后好统计真实的cost。所以选择扩大K倍,然后整除K,这样,+1会被直接舍掉。

K=n+1是防止n=1的情况被卡姿势。

代码

#include "iostream"
#include "cstdio"
#include "cstring"
#include "algorithm"
using namespace std;
#define maxn 55
int n,m,Hash[maxn],tmp,old,K;
int link[maxn],LX[maxn],RX[maxn],W[maxn][maxn],slack[maxn],e[maxn][maxn];
bool S[maxn],T[maxn];
const int inf=0x3f3f3f3f;
bool dfs(int u)
{
    S[u]=true;
    for(int v=1;v<=m;v++)
    {
        if(T[v]) continue;
        int t=LX[u]+RX[v]-W[u][v];
        if(!t)
        {
            T[v]=true;
            if(!link[v]||dfs(link[v]))
            {
                link[v]=u;
                return true;
            }
        }
        else if(t<slack[v]) slack[v]=t;
    }
    return false;
}
void update()
{
    int a=inf;
    for(int i=1;i<=m;i++) //ny
       if(!T[i]&&slack[i]<a) a=slack[i];
    for(int i=1;i<=n;i++)  //nx
        if(S[i]) LX[i]-=a;
    for(int i=1;i<=m;i++) //ny
    {
        if(T[i]) RX[i]+=a;
        else slack[i]-=a;
    }
}
void KM()
{
    memset(link,0,sizeof(link));
    memset(RX,0,sizeof(RX));
    for(int i=1;i<=n;i++) //nx
        for(int j=1;j<=m;j++) //ny
            LX[i]=max(LX[i],W[i][j]);
    for(int i=1;i<=n;i++) //nx
    {
        for(int j=1;j<=m;j++) //ny
            slack[j]=inf;
        while(1)
        {
            memset(S,0,sizeof(S));
            memset(T,0,sizeof(T));
            if(dfs(i)) break;
            else update();
        }
    }
    int res=0,change=0;
    for(int i=1;i<=m;i++) //ny
    {
       if(link[i])
       {
           res+=(W[link[i]][i]/K);
           if(Hash[i]!=link[i]) change++;
       }
    }
    printf("%d %d
",change,res-old);
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
       memset(W,0,sizeof(W));
       memset(Hash,0,sizeof(Hash));
       old=0;K=n+1;
       for(int i=1;i<=n;i++)
          for(int j=1;j<=m;j++)
        {
            scanf("%d",&W[i][j]);
            W[i][j]*=K;
        }
       for(int i=1;i<=n;i++)
       {
           scanf("%d",&tmp);
           old+=(W[i][tmp]/K);
           Hash[tmp]=i;
           W[i][tmp]++;
       }
       KM();
       memset(slack,0,sizeof(slack));
       memset(LX,0,sizeof(LX));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/neopenx/p/4539004.html