杭电2853 Assignment

         很容易看出这道题是二分图最优匹配,码出来代码后,悲剧的wa了。。。后来想到了错误,却一直想不到改进的方法。我开始的思路是,求出二分图最优匹配,然后判断新的匹配和原来的匹配有多少个不一样,即为需要调整的书目。后来想到这种方法的bug,如果有a、b两个公司和c、d两个任务,对应的值都为1  2,1  2,即权值相等。这样的话,用KM算法可能改变原来的匹配,实际上是不需要改变匹配的,即调整的数目为0,然而用KM算法,可能算出的结果需调整的数目为2。

       这道题的巧妙之处在于建图的巧妙,我们可以把每条权值都扩大一个倍数,如扩大K倍,K>n,n为公司的数量。这样最后算出的总权值/K即为实际的总权值。然后对题目中给出的匹配的每条边都加1,即在选择原来边的时候有优势,这样在用KM算法时,就会避免本来不需要改变的匹配被改变。即使是最优匹配为原匹配,最后求的结果为x*K+n,x为实际的总的匹配,因为K>n,所以用结果/K,取整,还是x,即为实际的总的最优匹配。求需要改变匹配的数量时,只需用n-求的结果%K即可。

题目:

Assignment

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


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
 

ac代码:

KM算法
#include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <climits>
#include <string.h>
#include <algorithm>
using namespace std;
int numcom,nummisson;//公司数量和任务数量
int map[55][55];//存储权值
int num[55];//存储公司所选任务的序号
int sx[55],sy[55];//存储顶点顶标的值
int slack[55];//存储最小值
int match[55];//判断是否被匹配的数组
int vistedx[55],vistedy[55];//判断是否访问过顶点
void init(){//初始化函数
   memset(sx,0,sizeof(sx));
   memset(sy,0,sizeof(sy));
   memset(match,-1,sizeof(match));
   memset(slack,0,sizeof(slack));
   for(int i=1;i<=numcom;++i){
	   int x=map[i][1];
	   for(int j=1;j<=nummisson;++j){
	     if(x<map[i][j])
			 x=map[i][j];
	   }//for
	   sx[i]=x;//左面顶标的值为边中的最大权值
   }//for
}//init
bool dfs(int x){//找增广路的函数
  vistedx[x]=true;//表明该顶点已经被访问过
  for(int i=1;i<=nummisson;++i){
    int xx=sx[x]+sy[i]-map[x][i];
	if(!vistedy[i]&&xx==0){//顶点没被访问过,且符合最优匹配加边的条件
	    vistedy[i]=1;
		if(match[i]==-1||dfs(match[i])){
		  match[i]=x;
		  return true;
		}//if
	}//if
	else if(slack[i]>xx)//slack[i]取最小值
		slack[i]=xx;
  }//for
  return false;
}//dfs
void bestmatch(){//最优匹配函数
	for(int i=1;i<=numcom;++i){
		while(1){//一直找到匹配为止
		  memset(vistedx,0,sizeof(vistedx));
		  memset(vistedy,0,sizeof(vistedy));
		  for(int j=1;j<=nummisson;++j)
			  slack[j]=INT_MAX;
		  if(dfs(i)){//找到匹配,跳出while循环
		    break;
		  }//if
		  int dd=INT_MAX;
		  for(int j=1;j<=nummisson;++j){
		    if(!vistedy[j]&&dd>slack[j])//顶点没在增广路中且取最小值
				dd=slack[j];
		  }//for
		  for(int j=1;j<=numcom;++j){
		    if(vistedx[j])
				sx[j]-=dd;
		  }//for
		  for(int j=1;j<=nummisson;++j){
		    if(vistedy[j])
				sy[j]+=dd;
		  }
		}//while
	}//for
}//bestmatch
int main(){
	//freopen("1.txt","r",stdin);
	while(~scanf("%d%d",&numcom,&nummisson)){
	  int x;
	  for(int i=1;i<=numcom;++i){
		  for(int j=1;j<=nummisson;++j){
		    scanf("%d",&x);
			map[i][j]=x;
			map[i][j]*=100;//将权值都扩大100倍
		  }//for
	  }//for
	  int total=0;//定义总量初始值
	  for(int i=1;i<=numcom;++i){
	    scanf("%d",&num[i]);
		total+=map[i][num[i]];
		map[i][num[i]]+=1;//原来的边有优势
	  }//for
	  init();//初始化函数
	  bestmatch();//最优匹配
	  int sum=0;//初始化新的总量的值
	  int count=0;//初始化需要改变的公司的数量
	  for(int i=1;i<=nummisson;++i)
	  { 
		  if(match[i]<1||match[i]>50)continue;
		  sum+=map[match[i]][i];
	  }//for
	  printf("%d %d\n",numcom-sum%100,sum/100-total/100);
	}//while
  return 0;
}//main


你可能感兴趣的:(杭电2853 Assignment)