HDU 2853 Assignment【二分图最优匹配+巧妙权值】

HDU 2853  Assignment【二分图最优匹配+巧妙权值】http://acm.hdu.edu.cn/showproblem.php?pid=2853

Problem Description
Last year a terrible earthquake attacked Sichuan province. About 300,000 PLA soldiers attended the rescue, also ALPCs. Our mission is to solve difficulty problems to optimization the assignment of troops. The assignment is measure by efficiency, which is an integer, and the larger the better.
We have N companies of troops and M missions, M>=N. One company can get only one mission. One mission can be assigned to only one company. If company i takes mission j, we can get efficiency Eij. 
We have a assignment plan already, and now we want to change some companies’ missions to make the total efficiency larger. And also we want to change as less companies as possible.
 

Input
For each test case, the first line contains two numbers N and M. N lines follow. Each contains M integers, representing Eij. The next line contains N integers. The first one represents the mission number that company 1 takes, and so on.
1<=N<=M<=50, 1<Eij<=10000.
Your program should process to the end of file.
 

Output
For each the case print two integers X and Y. X represents the number of companies whose mission had been changed. Y represents the maximum total efficiency can be increased after changing.
 

Sample Input
  
  
3 3 2 1 3 3 2 4 1 26 2 2 1 3 2 3 1 2 3 1 2 3 1 2
 

Sample Output
  
  
2 26 1 2
 

Source
 

【题意】n个公司,m个任务,n*m的矩阵元素map[i][j]表示第i公司做第j任务时的效率,每个公司只能做一项任务,并且一项任务只能由一个公司去做,为了效率最大化,已知n个公司原先分配的任务,求怎么改变现有任务分配使效率最优化,求最少的改变次数,及比原先提高的效率和~

【思路】略屌的想法——巧妙的思路:

因为我们要变动最小,所以对在原计划中的边要有一些特殊照顾,使得最优匹配时,尽量优先使用原计划的边,这样变化才能是最小的且不会影响原匹配。

根据这个思想,我们可以把每条边的权值扩大k倍,k要大于n。然后对原计划的边都+1。精华全在这里。我们来详细说明一下。

全部边都扩大了k倍,而且k比n大,这样,我们求出的最优匹配就是k倍的最大权值,只要除以k就可以得到最大权值。实现原计划的边加1,这样,在每次选择边时,这些变就 有了优势,就会优先选择这些边。假如原计划的h条边被选入了最优匹配中,这样,最优权值就是k倍的最大权值+k(原计划的每条边都+1)。但是k大于n的用意何在呢?我们发现假如原计划的边全部在匹配中,只会增加n,又n<k,所以除以k后不会影响最优匹配的最大权值之和,然后我们对k取余,就正好得到加入的原计划的边的个数。这时,我们只需要用总点数-加入的原计划的点数,就可以求得最小变动数了。

思路果然巧妙,既然自己想不出来,就见一个记住一个吧。积少成多。

【代码如下】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <climits>
#include <cmath>

using namespace std;

const int N = 55;
int lx[N],ly[N],match[N],slack[N],map[N][N];
bool visx[N],visy[N];
int n,m;

bool dfs(int x)
{
    visx[x] = true;
    for(int i=1; i<=m; i++)
    {
        if(visy[i]) continue;
        int t = lx[x] + ly[i] - map[x][i];
        if(!t)
        {
            visy[i] = true;
            if(match[i] == -1 || dfs(match[i]))
            {
                match[i] = x;
                return true;
            }
        }
        else
            slack[i] = min(slack[i],t);
    }
    return false;
}

void km()
{
    int temp;
    memset(lx, 0, sizeof(lx));
    memset(ly, 0, sizeof(ly));
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            lx[i] = max(lx[i], map[i][j]);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++) slack[j] = INT_MAX;
        while(1)
        {
            memset(visx, 0, sizeof(visx));
            memset(visy, 0, sizeof(visy));
            if(dfs(i))
                break;
            else{
                temp = INT_MAX;
                for(int j=1; j<=m; j++)
                {
                    if(!visy[j] && temp > slack[j])
                        temp = slack[j];
                }
                for(int j=1; j<=n; j++)
                    if(visx[j]) lx[j] -= temp;
                for(int j=1; j<=m; j++)
                {
                    if(visy[j]) ly[j] += temp;
                    else slack[j] -= temp;
                }
            }
        }
    }
}

int main()
{
    int ans, res;
    while(scanf("%d %d", &n, &m) != EOF)
    {
        ans = 0; res = 0;
        memset(match,-1,sizeof(match));
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++){
                scanf("%d",&map[i][j]);
                map[i][j] *= 100;
            }
        for(int i=1; i<=n; i++)
        {
            int ss;
            scanf("%d", &ss);
            res += map[i][ss];
            map[i][ss] += 1;
        }

        km();
        for(int i=1; i<=m; i++)
            if(match[i]!=-1) ans += map[ match[i] ][i];

        printf("%d %d\n", n-ans%100, (ans-res)/100);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值