hdu 2853

来源:互联网 发布:战争框架淘宝mod 编辑:程序博客网 时间:2024/06/04 01:16

Assignment

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


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 32 1 33 2 41 26 22 1 32 31 2 31 2 31 2
 

Sample Output
2 261 2
 

这题运用的kM模板不难,主要考察的是如何解决最少改变原匹配数,对思维的要求很高


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 201;
int lx[N], ly[N], visx[N], visy[N], match[N], wight[N][N], slack[N], val[N], visit[N][N];
int dfs(int x);
int n, m;
const int inf = 0x3f3f3f3f;
void km();


int main()
{
    while(scanf("%d %d", &n, &m)!=EOF)
    {
        memset(wight,0,sizeof(wight));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                scanf("%d",&wight[i][j]);
            }
        }
        memset(match,-1,sizeof(match));
        memset(visit,0,sizeof(visit));
        int sum=0, sum1=0;
        for(int i=1; i<=n; i++)
        {
            int v;
            scanf("%d", &v);
            sum1+=wight[i][v];
            visit[i][v]=1;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(visit[i][j])
                {
                    wight[i][j]=wight[i][j]*(n+1)+1;
                }
                else
                {
                    wight[i][j]=wight[i][j]*(n+1);
                }
            }
        }
        km();
        for(int i=1;i<=m;i++)
        {
            if(match[i]!=-1)
            {
                sum+=wight[match[i]][i];
            }
        }
        printf("%d %d\n",n-sum%(n+1),sum/(n+1)-sum1);
    }
    return 0;
}


void km()
{
    memset(ly,0,sizeof(ly));
    for(int i=1; i<=n; i++)
    {
        lx[i]= -inf;
        for(int j=1; j<=m; j++)
        {
            lx[i]=max(wight[i][j],lx[i]);
        }
    }
    for(int i=1; i<=n; i++)
    {
        memset(slack,0x3f3f,sizeof(slack));
        while(1)
        {
            memset(visx,0,sizeof(visx));
            memset(visy,0,sizeof(visy));
            if(dfs(i))
            {
                break;
            }
            else
            {
                int tmp=inf;
                for(int j=1; j<=m; j++)
                {
                    if(!visy[j])
                    {
                        tmp=min(tmp,slack[j]);
                    }
                }
                for(int j=1; j<=m; j++)
                {
                    if(visx[j])
                    {
                        lx[j]-=tmp;
                    }
                    if(visy[j])
                    {
                        ly[j]+=tmp;
                    }
                    else
                    {
                        slack[j]-=tmp;
                    }
                }
            }
        }
    }
}




int dfs(int x)
{
    visx[x]=1;
    for(int i=1; i<=m; i++)
    {
        if(visy[i])
        {
            continue;
        }
        if(lx[x]+ly[i]==wight[x][i])
        {
            visy[i]=1;
            if(match[i]==-1||dfs(match[i]))
            {
                match[i]=x;
                return 1;
            }
        }
        else
        {
            slack[i]=min(slack[i],lx[x]+ly[i]-wight[x][i]);
        }
    }
    return 0;
}


 首先如果我们只需要求最大权值匹配,那么这道题是一个左右点集大小不对称的最优匹配问题。这里要注意KM算法模板的修改。

       最优权值匹配很好求,直接用KM模板,但是要在原匹配边的基础上使得改变的边最少,这里我们这么处理:

       这里由于左边点集有N个点,且M>=N。那么最终的最优匹配必然有N条边。我们让原图中的每条边的权值都乘以(N+1),即扩大N+1倍。且如果某条边本来就是原匹配用的其中一条边,那么该边权值在扩大N+1倍后,再加1。 所以任意一条边的权值只能是N+1的倍数 或 (N+1的倍数)+1,我们将要在这种权值的边中选出N条来(想想如果我们最终在新二分图求出的最优权值和==(N+1)的倍数,那么说明什么?说明我们最优匹配中,一条老边都没有复用.虽然老边的权值有加1的优势 )。

       最终我们得到的最优权值和ans除以(N+1)就是最优权值解(因为就算我们原封不动的还用以前的匹配,也就是在所有权值的基础上加了N1,此时/(N+1),整除归0)。

        最终ans%(N+1)就是我们复用旧边的条数(上述两条结论仔细验证)。

        下面来论证一下为什么上面的所有边权值*(N+1),老边还+1”这个策略可行?

        假设原图有唯一最优权值的解(即所有其他的匹配方案边的权值总和都要小于该解的权值和)。

        那么所有边权值*(N+1),老边还+1”之后.假设原先次优解的权值只比最优解的权值少1,

        且次优解匹配用的都是原匹配边(即操作后次优解权值和增加的最多)

        且最优解匹配用的都是原非匹配边(即最优解权值和增加的最少). 

        就算是这样,操作后,最优解的权值依然正好比次优解权值和大1. 

        所以如果最优解唯一,我们不会丢失最优解.

       下面假设原图最优解方案不唯一,即有多个最优解方案可以得到最优解. 当时我们能肯定 那个用到老边最多的最优解方案 在我们所有边权值*(N+1),老边还+1”操作后,其权值必然是所有解里面最大的. 即它变成了新的最优解,我们自然选它.

       上述两段论证了为什么我们所有边权值*(N+1),老边还+1”这个操作的可行性.


0 0