hdu 2853 Assignment【KM+思维】好题

本文介绍了一个关于地震救援中军队任务分配的问题,旨在通过优化算法提高救援效率。文章详细解析了问题背景、输入输出格式及样例,并给出了AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Assignment

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

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

2009 Multi-University Training Contest 5 - Host by NUDT

 

题目大意:

给你一个n*m的矩阵,保证n<=m,然后给你一个原匹配,让你求一个最优匹配,使得这个最优匹配的匹配上的人和给出的原匹配尽量的相似,让你求出不相似的人的个数,以及最优匹配和原匹配的差值。


分析样例:

对应第一组:

2 1 3

3 2 4

1 26 2

显然有两种匹配方式能够达到最优匹配(32):

①第一行第三个,第二行第一个,第三行第二个。不相似的个数:2

②第一行第一个,第二行第三个,第三杭第二个。不相似的个数:3

那么尽量更小的一样,那么输出2 32-原匹配(6) 2 26;


思路:


1、思路来源自网络,脑洞题,菜鸡做不出 。


2、首先分析这样一个问题:在最优匹配问题上,想要得到一个最优匹配,很明显需要一个突出的权值才能使得我们将其匹配上,那么我们考虑的重点就应该放在修改原矩阵权值上来。那么如果我们想要修改权值,并且还想在修改权值之后在某种程度上得到的最优匹配和原矩阵的最优匹配值相同,那么我们设定一个值k,令原矩阵的所有值都乘以k,得到的最优匹配值ans/k==原矩阵的最优匹配。


3、那么我们又要如何保证尽量匹配的结果和原匹配相似呢?其实这个时候我们可以在原匹配的那些位子上的权值都加一,使得这些值被匹配上更加有优势(相对于同权值匹配更加有优势,但是一定不会影响更优匹配)。那么我们在这些位子上权值都加了一,那么最终匹配的权值和如何保证/k之后和原矩阵的最优匹配值相同呢?这就要保证k大于n才行。这样,我们既能在同等权值匹配的条件下尽量选择我们的原匹配,还能保证了得到的权值和/k与原矩阵的匹配值相同。


4、那么相似的匹配个数其实就是ans%k;那么其不相似的个数:n-ans%k;最优匹配的值和原匹配的值相差:ans/k-原匹配的值,为了问题简化,我们设定k为100即可。


Ac代码:


#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int a[500][500];
int nn,mm,k;
int tmp[500];
int lx[500];
int ly[500];
int vx[500];
int vy[500];
int match[500];
int low;
int find(int u)
{
    vx[u]=1;
    for(int i=1;i<=mm;i++)
    {
        if(vy[i]==1)continue;
        int tmp=lx[u]+ly[i]-a[u][i];
        if(tmp==0)
        {
            vy[i]=1;
            if(match[i]==-1||find(match[i]))
            {
                match[i]=u;
                return 1;
            }
        }
        else if(tmp<low)low=tmp;
    }
    return 0;
}
int KM()
{
    memset(match,-1,sizeof(match));
    memset(lx,0,sizeof(lx));
    memset(ly,0,sizeof(ly));
    for(int i=1;i<=nn;i++)
    {
        for(int j=1;j<=mm;j++)
        {
            lx[i]=max(lx[i],a[i][j]);
        }
    }
    for(int i=1;i<=nn;i++)
    {
        while(1)
        {
            low=0x3f3f3f3f;
            memset(vx,0,sizeof(vx));
            memset(vy,0,sizeof(vy));
            if(find(i))break;
            for(int j=1;j<=nn;j++)
            {
                if(vx[j])lx[j]-=low;
            }
            for(int j=1;j<=mm;j++)
            {
                if(vy[j])ly[j]+=low;
            }
        }
    }
    int sum=0;
    for(int i=1;i<=mm;i++)
    {
        if(match[i]==-1)continue;
        sum+=a[match[i]][i];
    }
    printf("%d ",nn-sum%100);
    return sum;
}
int main()
{
    while(~scanf("%d%d",&nn,&mm))
    {
        for(int i=1;i<=nn;i++)
        {
            for(int j=1;j<=mm;j++)
            {
                scanf("%d",&a[i][j]);
                a[i][j]*=100;
            }
        }
        int sum2=0;
        for(int i=1;i<=nn;i++)
        {
            scanf("%d",&tmp[i]);
            sum2+=a[i][tmp[i]]/100;
            a[i][tmp[i]]++;
        }
        int sum=KM();
        printf("%d\n",sum/100-sum2);
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值