hdu 2853

Assignment

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


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個任務,每個公司完成每個任務的收益都不同。現在已經有一個分配計劃,問能否變動最少的分配計劃,使得收益更大。輸出變動的數量以及增加的收益。
思路來自於網上,經典在於所有邊權值擴大K(k>n)倍,原來在計劃內的邊權值加1,這樣就保證了選擇上的優先性。其他的就是km模板了。
/*
因爲我們要變動最小,所以對在原計劃中的邊要有一些特殊照顧,
使得最優匹配時,儘量優先使用原計劃的邊,這樣變化才能是最小的且不會影響原匹配。
根據這個思想,我們可以把每條邊的權值擴大k倍,k要大於n。
然後對原計劃的邊都+1。精華全在這裏。我們來詳細說明一下。
全部邊都擴大了k倍,而且k比n大,這樣,我們求出的最優匹配就是k倍的最大權值,
只要除以k就可以得到最大權值。實現原計劃的邊加1,這樣,在每次選擇邊時,
這些變就有了優勢,就會優先選擇這些邊。假如原計劃的h條邊被選入了最優匹配中,這樣,
最優權值就是k倍的最大權值+k(原計劃的每條邊都+1)。但是k大於n的用意何在呢?
我們發現假如原計劃的邊全部在匹配中,只會增加n,又n<k,
所以除以k後不會影響最優匹配的最大權值之和,
然後我們對k取餘,就正好得到加入的原計劃的邊的個數。
這時,我們只需要用總點數-加入的原計劃的點數,就可以求得最小變動數了。
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 99999999
#define maxn 305

int lx[maxn],ly[maxn];//頂標
int Match[maxn];//記錄匹配值
int visx[maxn],visy[maxn];
int w[maxn][maxn];//權值
int slack[maxn];//slack爲修改量
int ans,n,m;

bool findPath(int x)//尋找最優解
{
    int temp;
    visx[x]=1;
    for(int y=1; y<=m; y++)
    {
        if(visy[y])continue;
        if(w[x][y]==lx[x]+ly[y])//說明是相等子圖
        {
            visy[y]=1;
            if(!Match[y]||findPath(Match[y]))
            {
                Match[y]=x;
                return true;
            }
        }
        else
        slack[y]=min(slack[y],lx[x]+ly[y]-w[x][y]);
    }
    return false;
}
void km()
{
    memset(Match,0,sizeof(Match));
    //初始化頂標
    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],w[i][j]);//
    for(int x=1; x<=n; x++)
    {
        for(int i=1;i<=m;i++)
        slack[i]=INF;
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(findPath(x))break;
            int tmp=INF;
            for(int i=1;i<=m;i++)
            {
                if(!visy[i])
                {
                    if(tmp>slack[i])
                    tmp=slack[i];
                }
            }
            if(tmp==INF)return ;
            for(int i=1; i<=m; i++)
            {
                if(visx[i]) lx[i]-=tmp;
                if(visy[i]) ly[i]+=tmp;
                else
                slack[i]-=tmp;
            }
        }
    }
}
int main()
{
    int tt,sum;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        ans=0;
        sum=0;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&w[i][j]);
                w[i][j]*=100;
            }
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tt);
            w[i][tt]++;
            sum+=w[i][tt];
        }
        km();
        for(int i=1; i<=m; i++)
            ans+=w[Match[i]][i];
        printf("%d %d\n",sum%100-ans%100,ans/100-sum/100);
    }
    return 0;
}

發佈了99 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章