Assignment (HDU 2853 最大权匹配KM)

11 篇文章 0 订阅


Assignment

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


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
 

Recommend
gaojie   |   We have carefully selected several similar problems for you:   2855  2854  2856  2857  2858 


题意:n个公司m个任务,每个公司只接受一个任务,每个任务只被一个公司接受,每个公司i接受任务j的做事效率为g[i][j],现在已经分配好了,问怎样改变任务分配可以让效率最大,求出要改动的公司数目和增加的效率。

思路:与hdu3315相似,hdu 3315若点数为N,则把每条边的权值扩大x倍(x>N),若是原有匹配,则再把权值加1。最后KM算法求出ans,则最大权值之和=ans/x,没有被改动的=ans%x。

代码:


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define maxn 1005
#define MAXN 2005
#define mod 1000000009
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-6
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define FRE(i,a,b)  for(i = a; i <= b; i++)
#define FREE(i,a,b) for(i = a; i >= b; i--)
#define FRL(i,a,b)  for(i = a; i < b; i++)
#define FRLL(i,a,b) for(i = a; i > b; i--)
#define mem(t, v)   memset ((t) , v, sizeof(t))
#define sf(n)       scanf("%d", &n)
#define sff(a,b)    scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf          printf
#define DBG         pf("Hi\n")
typedef long long ll;
using namespace std;

/*
KM算法 O(nx*nx*ny)
求最大权匹配(最佳匹配)
若求最小权匹配,可将权值取相反数,结果取相反数
点的标号从0开始
*/

const int N=110;
int nx,ny;      //两边的点数
int g[N][N];    //二分图描述,g赋初值为-INF
int linker[N],lx[N],ly[N];  //y 中各点匹配状态,x,y中的点的标号
int slack[N];
bool visx[N],visy[N];
bool flag;

bool DFS(int x)
{
    visx[x]=true;
    for (int y=0;y<ny;y++)
    {
        if (visy[y]) continue;
        int tmp=lx[x]+ly[y]-g[x][y];
        if (tmp==0)
        {
            visy[y]=true;
            if (linker[y]==-1||DFS(linker[y]))
            {
                linker[y]=x;
                return true;
            }
        }
        else if (slack[y]>tmp)
            slack[y]=tmp;
    }
    return false;
}

int KM()
{
    flag=true;
    memset(linker,-1,sizeof(linker));
    memset(ly,0,sizeof(ly));
    for (int i=0;i<nx;i++) //赋初值,lx置为最大值
    {
        lx[i]=-INF;
        for (int j=0;j<ny;j++)
        {
            if (g[i][j]>lx[i])
                lx[i]=g[i][j];
        }
    }
    for (int x=0;x<nx;x++)
    {
        for (int i=0;i<ny;i++)
            slack[i]=INF;
        while (true)
        {
            memset(visx,false,sizeof(visx));
            memset(visy,false,sizeof(visy));
            if (DFS(x)) break;
            int d=INF;
            for (int i=0;i<ny;i++)
                if (!visy[i]&&d>slack[i])
                    d=slack[i];
            for (int i=0;i<nx;i++)
                if (visx[i])
                    lx[i]-=d;
            for (int i=0;i<ny;i++)
            {
                if (visy[i])
                    ly[i]+=d;
                else
                    slack[i]-=d;
            }
        }
    }
    int res=0;
    for (int i=0;i<ny;i++)
    {
        if (linker[i]==-1||g[linker[i]][i]<=-INF) //有的点不能匹配的话return-1
        {
            flag=false;
            continue;
        }
        res+=g[linker[i]][i];
    }
    return res;
}
//记得nx和ny初始化!!!!!!!!

int n,m;

int main()
{
#ifndef ONLINE_JUDGE
    freopen("C:/Users/asus1/Desktop/IN.txt","r",stdin);
#endif
    int i,j,x;
    while (~sff(n,m))
    {
        nx=n;
        ny=m;
        int sum=0;
        for (i=0;i<n;i++)
            for (j=0;j<m;j++)
        {
            sf(x);
            g[i][j]=x*100;
        }
        for (i=0;i<n;i++)
        {
            sf(x);x--;
            sum+=g[i][x];
            g[i][x]++;
        }
        int ans=KM();
        printf("%d %d\n",n-ans%100,ans/100-sum/100);
    }
    return 0;
}
/*
3 3
2 1 3
3 2 4
1 26 2
2 1 3
2 3
1 2 3
1 2 3
1 2
*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值