zoukankan      html  css  js  c++  java
  • hdu2853 Assignment(KM+双元限制)

    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

    分析:
    这道题有两个限制唉,然而我们好像研究过双元限制的题目

    题目要求在最大效率的前提下,改动最少,我们第一步就是把两个量统一(同大或同小)
    设最大效率为v1,不改动的为v2
    显然有柿子X=M*v1+v2
    因为任务和部队之间是一一配对的,我们就可以用KM求解一个最大X

    我这样说,好像是挺简单
    然而我们在实际处理的时候
    需要把所有的效率值*M
    但是这样只解决了X的前半部分,

    对于每一个已经安排了的任务,
    效率值++
    这样我们就可以在前半部分相同的情况下,改动最少了(等价于不改动最多)

    ME的最大值(10000)就可以了

    tip

    这道题还是很巧妙的,以前不知道双元限制的处理方法的时候,对于这道题是完全没招的
    这道题可以作为解决双元限制的模板了

    //这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace std;
    
    const int INF=0x33333333;
    int W[55][55];
    int Lx[55],Ly[55],belong[55],slack[55];
    bool L[55],R[55];
    int n,m;
    
    int match(int i)
    {
        L[i]=1;
        for (int j=1;j<=m;j++)
            if (!R[j])   //Y部每个点只匹配一遍 
            {
                int v=Lx[i]+Ly[j]-W[i][j];
                if (!v)
                {
                    R[j]=1;  //参与匹配的标记 
                    if (!belong[j]||match(belong[j]))
                    {
                        belong[j]=i;
                        return 1;
                    }
                }
                else slack[j]=min(v,slack[j]);   //记录一下如果需要修改,顶标的改变量 
            }
        return 0;
    }
    
    int KM()   //X部元素个数:n,Y部元素个数:m 
    {
        memset(belong,0,sizeof(belong));   //Y部在X部的匹配元素 
        for (int i=1;i<=n;i++)   //顶标 
        {
            Lx[i]=W[i][1];  //防止有负边权 
            Ly[i]=0;  
            for (int j=2;j<=m;j++) Lx[i]=max(Lx[i],W[i][j]);
        }
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=m;j++) slack[j]=INF;   //(德尔塔)顶标 
            while (1)
            {
                memset(L,0,sizeof(L));
                memset(R,0,sizeof(R));
                if (match(i)) break;
                int a=INF;
                for (int j=1;j<=m;j++)
                    if (!R[j]) a=min(a,slack[j]);   //寻找新顶标,新顶标只与没有参与匹配的Y点有关 
                for (int j=1;j<=n;j++)
                    if (L[j]) Lx[j]-=a;
                for (int j=1;j<=m;j++)
                    if (R[j]) Ly[j]+=a; 
                    else slack[j]-=a;    //
            }
        }
        int ans=0;
        for (int i=1;i<=m;i++) ans+=W[belong[i]][i];
        return ans;
    }
    
    int main()
    {
        while (scanf("%d%d",&n,&m)!=EOF)
        {
            for (int i=1;i<=n;i++)
                for (int j=1;j<=m;j++)
                    scanf("%d",&W[i][j]),W[i][j]*=10000;
            int sum=0;
            for (int i=1;i<=n;i++)
            {
                int x;
                scanf("%d",&x);
                sum+=W[i][x];
                W[i][x]++;
            }
            sum/=10000;
            int ans=KM();
            printf("%d %d
    ",n-ans%10000,ans/10000-sum);   //改动数,最大效率 
            KM();
        }
        return 0;
    } 
  • 相关阅读:
    PHP程序员应该知道的15个库
    MongoDB、Cassandra 和 HBase 三种 NoSQL 数据库比较
    四种常见的POST提交数据方式
    PHP中获取文件扩展名的N种方法
    【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群(三)
    【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群(二)
    【问底】徐汉彬:亿级Web系统搭建——单机到分布式集群(一)
    ZF框架数据对象映射模式的思考
    概念大集合:单一入口、MVC、ORM、CURD、ActiveRecord...
    有默认参数的函数,默认参数为何需后置
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673045.html
Copyright © 2011-2022 走看看