HDU Assignment(KM变形好题!!!!)



                                                                                Assignment


题目链接:Click Here~

题目分析:

   给出n个人m个任务(n<=m),要你给每个人分配任务,每个人对每个任务都有一个效益。要求你求出n个人的最大效益。且一开始每个人手上都有一个任务,你在以后分配任务的时候,必须更换尽量少的任务使得每个人的任务效益最大。


算法分析:

   一道显然的二分匹配算法。用KM求出最大的效益就好了。


建模分析:

  因为,在更换最少的任务前提下的最大效益。所以,我们可以把每个任务扩大K倍(K>n)。为了使得尽量保持原先分配的任务,所以要给原先分配的任务+1。为什么这样可以呢?因为,前面我们扩大了K倍了,且K>n。所以,实质+1这个只对原先相等的权值有作用的,这就是为什么要K>n的原因。最后,在把结果除以K,就是结果。总结点减去结果求余K就是改变的数。为什么呢?因为有数学知识知道原先的每个权值都乘以了一个K。所以,不是原先分配的任务都可以被K除尽,唯一剩下的就是原先分配剩下的那个+1的值。


   ACM果然是个一群高智商的人,玩的蛋疼游戏!!!


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

const int MAXN = 50 + 5;
const int INF = ~0U>>1;
const int K = MAXN+MAXN;
int n,m,W[MAXN][MAXN];
int slack,Lx[MAXN],Ly[MAXN],Link[MAXN];
bool S[MAXN],T[MAXN];
inline bool Match(int i)
{
    S[i] = true;
    for(int j = 1;j <= m;++j)if(!T[j]){
        int tmp = Lx[i]+Ly[j]-W[i][j];
        if(tmp==0){
            T[j] = true;
            if(Link[j]==-1||Match(Link[j])){
                Link[j] = i;
                return true;
            }
        }
        else if(tmp < slack)
            slack = tmp;
    }
    return false;
}
inline void Update(int d)
{
    for(int i = 1;i <= n;++i)
        if(S[i]) Lx[i] -= d;
    for(int j = 1;j <= m;++j)
        if(T[j]) Ly[j] += d;
}
bool EK()
{
    for(int i = 1;i <= n;++i){
        Lx[i] = 0;
        for(int j = 1;j <= m;++j)
            Lx[i] = max(Lx[i],W[i][j]);
    }
    memset(Link,-1,sizeof(Link));
    memset(Ly,0,sizeof(Ly));

    slack = INF;
    for(int i = 1;i <= n;++i){
        while(1){
            memset(S,false,sizeof(S));
            memset(T,false,sizeof(T));
            if(Match(i))
                break;
            if(slack==INF)
                return false;
            Update(slack);
        }
    }
    return true;
}
void Get(int& X,int& Y)
{
    int sum = 0;
    for(int i = 1;i <= m;++i){
        if(Link[i]!=-1)
            sum += W[Link[i]][i];
    }
    Y = sum/K - Y;
    X = n - sum%K;
}
int main()
{
//    freopen("Input.txt","r",stdin);
//    freopen("Out.txt","w",stdout);
    while(~scanf("%d%d",&n,&m))
    {
        memset(W,0,sizeof(W));
        for(int i = 1;i <= n;++i){
            for(int j = 1;j <= m;++j){
                scanf("%d",&W[i][j]);
                W[i][j] *= K;
            }
        }
        int x,X,Y = 0;
        for(int i = 1;i <= n;++i){
            scanf("%d",&x);
            Y += W[i][x]/K;
            W[i][x]++;
        }
        EK();
        Get(X,Y);
        printf("%d %d\n",X,Y);
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值