hdu 2853 Assignment(最大权值匹配)

20 篇文章 0 订阅

Assignment

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 757    Accepted Submission(s): 401


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


 

题意:有n个人和m个任务,要给每个人分配一个任务,一个任务只能分配给一个人,一个人只能接一个任务。每个人接不同的任务都有一个相应的权值。现已有一个分配计划,要求重新分配任务,使分配后的总权值最大,而且与原分配的变化要尽量小。输出分配有变化的人的数量,和重新分配后权值的变化。

思路:二分图最大权值匹配。题目要求使变化尽量小,所以当权值相同时,要优先选择原匹配的边。做法是先使每条边的权值*100,然后原匹配的边+1,最后n-最大权值和%100即为有变化的人数。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdlib>
#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)
#define ll long long
#define eps 1e-6
using namespace std;

const int maxn=55;
const int INF=1000000000;
int G[maxn][maxn],lx[maxn],match[maxn],ly[maxn],slack[maxn];
bool visx[maxn],visy[maxn];
int n,m;
bool find(int u)
{
    visx[u]=true;
    for(int i=1; i<=m; i++)
    {
        if(visy[i]) continue;
        if(lx[u]+ly[i]==G[u][i])
        {
            visy[i]=true;
            if(match[i]==-1||find(match[i]))
            {
                match[i]=u;
                return true;
            }
        }
        else slack[i]=min(slack[i],lx[u]+ly[i]-G[u][i]);
    }
    return false;
}
void KM()
{
    memset(match,-1,sizeof(match));
    for(int i=1; i<=n; i++) lx[i]=-INF;
    memset(ly,0,sizeof(ly));
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            lx[i]=max(lx[i],G[i][j]);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++) slack[j]=INF;
        while(1)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if(find(i)) break;
            else
            {
                int temp=INF;
                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 a;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&G[i][j]);
            G[i][j]*=100;
        }
        int tot=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            tot+=G[i][a];
            G[i][a]++;
        }
        KM();
        int sum=0;
        for(int i=1; i<=m; i++)
        if(match[i]!=-1) sum+=G[match[i]][i];
        printf("%d %d\n",n-sum%100,sum/100-tot/100);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值