HDU 2853 Assignment(二分图最大权匹配 KM算法)

55 篇文章 2 订阅
10 篇文章 0 订阅

其实对萌新来说很不友好...

写了半天过不了...

现在我也不是很懂 只是会匈牙利

多练几篇再说吧。

不懂的可以移步链接:https://www.cnblogs.com/logosG/p/logos.html

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn=505;
const int oo=1e9;
int lx[maxn], ly[maxn], vx[maxn], vy[maxn], match[maxn], slack[maxn];
int map[maxn][maxn];
int n, m;

bool find(int i)
{
    vx[i]=1;                      ///相等子图中X集合
    for(int j=1; j<=m; j++)
        if(!vy[j])
        {
            int t=lx[i]+ly[j]-map[i][j];
            if(t==0)       ///当这条边在相等子图中
            {
                vy[j]=1;                  ///相等子图中Y集合
                if(match[j]==-1||find(match[j]))
                {
                    match[j]=i;
                    return true;
                }
            }
            else slack[j]=min(slack[j],t);
        }
    return false;
}

int KM()
{
    int  ans=0;
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));
    for(int i=1; i<=n; i++)
    {
        lx[i]=-oo;
        for(int j=1; j<=m; j++)
            lx[i]=max(lx[i],map[i][j]);    ///顶标lx初始化保存的是连接i节点的最大边权值
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++) slack[j]=oo;
        while(1)
        {
            memset(vx,0,sizeof(vx));
            memset(vy,0,sizeof(vy));
            if(find(i)) break;
            int d=oo;
            for(int j=1; j<=m; j++)
                if(!vy[j]) d=min(d,slack[j]);
            for(int j=1; j<=n; j++)
                if(vx[j]) lx[j]-=d;
            for(int j=1; j<=m; j++)
                if(vy[j]) ly[j]+=d;
            for(int j=1; j<=m; j++) slack[j]-=d;
        }
    }
    for(int i=1; i<=m; i++)
        if(match[i]!=-1) ans+=map[ match[i] ][i];
    return ans;
}

int main()
{
    while(cin >> n >> m)
    {
        for(int i=0; i<=n; i++)
            for(int j=0; j<=m; j++) map[i][j]=-oo;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                 int c;
                 scanf("%d",&c);
                 map[i][j]=max(map[i][j],100*c);
            }
        int sum=0;
        for(int i=1,j; i<=n; i++)
        {
            scanf("%d",&j);
            sum+=map[i][j]/100;
            map[i][j]+=1;
        }
        int ans=KM();
        printf("%d %d\n",n-ans%100,ans/100-sum);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值