这是一道很值得去做二分图最佳匹配的题,题目不难理解,把问题模型转化成二分图模型也是常规的,但是对二分图的权值的变化是十分值得学习借鉴的。
这是我第二道二分图最佳匹配的题,通过这题可以发现,KM算法不仅仅可以针对二分图的完美匹配,也可以应用于使X集合中的每个点都饱和的匹配。
这道题首先要注意标号,题目的标号是从1开始,如果大家像我一样从0开始标号,就要注意标号的对应。这道题最大的特点在于给出一个初始匹配,而且希望最佳匹配中的边尽量选择自这个初始匹配。于是乎,关键就在于如何优先选择这些边。开始,我想通过针对每次匹配,优先提供初始匹配中的边,但结果是WA,所以只好求助于其他大神,然后才明白怎么处理。
处理的方法是通过改变权值使初始匹配中的边具有优先性。把二分图中的每条边扩大k倍,其中k>n。对于初始匹配中的边,每条边的权值再加1。通过这样的处理,如果原图中和初始匹配中的边权值相同,那么初始匹配中的边具有优先性;如果原图中比初始匹配中边的权值要大,那么不会影响其优先性。由于k>n,所以计算出的权值之和除以k,即为真正的最大权值之和(因为'/'为整除,多出来的会舍去),如果把结果模取k,则可以得到最佳匹配到底选中多少初始匹配中的边。(以上参考:HDU-2853 Assignment【二分图最优匹配】。)
代码(G++):
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define MAX 100
#define INF (1<<30)
using namespace std;
//#define LOCAL
int w[MAX][MAX],lx[MAX],ly[MAX],cy[MAX];
bool vx[MAX],vy[MAX];
int n,m,k;
void update()
{
int i,j,d;
d=INF;
for(i=0;i<n;i++) if(vx[i])
{
for(j=0;j<m;j++) if(!vy[j])
{
d=min(d,lx[i]+ly[j]-w[i][j]);
}
}
for(i=0;i<m;i++)
{
if(i<n&&vx[i]) lx[i]-=d;
if(vy[i]) ly[i]+=d;
}
}
bool match(int u)
{
int i,t;
vx[u]=true;
for(i=0;i<m;i++)
{
if(w[u][i]==lx[u]+ly[i]&&!vy[i])
{
vy[i]=true;
if(cy[i]==-1||match(cy[i]))
{
cy[i]=u;
return true;
}
}
}
return false;
}
int km()
{
int i,j,ans;
for(i=0;i<n;i++)
{
lx[i]=-INF;
for(j=0;j<m;j++) lx[i]=max(lx[i],w[i][j]);
}
for(i=0;i<m;i++)
{
ly[i]=0;
cy[i]=-1;
}
for(i=0;i<n;i++)
{
while(1)
{
memset(vx,false,sizeof(vx));
memset(vy,false,sizeof(vy));
if(match(i)) break;
else update();
}
}
ans=0;
for(i=0;i<m;i++)
{
if(cy[i]!=-1) ans+=w[cy[i]][i];
}
return ans;
}
int main(int argc, char *argv[])
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int i,j,sum,tmp,res;
while(scanf("%d %d",&n,&m)!=EOF)
{
k=n+1;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
scanf("%d",&w[i][j]);
w[i][j]*=k;
}
}
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",&tmp);
sum+=w[i][--tmp]/k;
w[i][tmp]++;
}
res=km();
printf("%d %d\n",n-res%k,res/k-sum);
}
system("PAUSE");
return EXIT_SUCCESS;
}
题目( http://acm.hdu.edu.cn/showproblem.php?pid=2853):
Assignment
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
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.
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.
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