http://poj.org/problem?id=2400
KM最大匹配
第一次KM的题目
详解就不说了 可以看这里:http://blog.163.com/huangbingliang@yeah/blog/static/94161399201011291044527/
我的代码和思路基本上市抄了别人的
不过还是要整理一下
1,KM 求最佳匹配
2,dfs求所有答案
还是看代码吧:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAX=0x7ffffff;
const int N=17;
int point[N][N];
int a[N],b[N];//二分图分两组 两组的顶标
int f[N];//指向
int sum;//最佳匹配答案
int I,n;
bool lv[N],rv[N];//是否在交叉树内
bool dfs(int x)//匈牙利算法
{
lv[x]=true;
for(int i=1;i<=n;++i)
{
if(!rv[i]&&a[x]+b[i]==point[x][i])
{
rv[i]=true;
if(f[i]==-1||dfs(f[i]))
{
f[i]=x;
return true;
}
}
}
return false;
}
int KM()
{
memset(b,0,sizeof(b));
for(int i=1;i<=n;++i)
{
a[i]=-MAX;
for(int j=1;j<=n;++j)
{
a[i]=max(a[i],point[i][j]);//a组顶标找最大
}
}
memset(f,-1,sizeof(f));
for(int l=1;l<=n;++l)
{
while(1)
{
memset(lv,false,sizeof(lv));
memset(rv,false,sizeof(rv));
if(dfs(l))//如果第l个找成功 跳出循环找下一个,否则修改顶标继续找
break;
int d=MAX;
for(int i=1;i<=n;++i)
{
if(lv[i])
{
for(int j=1;j<=n;++j)
{
if(!rv[j])
{
d=min(d,a[i]+b[j]-point[i][j]);//确定修改顶标最小值
}
}
}
}
for(int i=1;i<=n;++i)
{
if(lv[i])
a[i]-=d;
if(rv[i])
b[i]+=d;//修改顶标
}
}
}
sum=0;
for(int i=1;i<=n;++i)
{
if(f[i]!=-1)
{
sum+=(-point[f[i]][i]);//最近匹配答案
}
}
return sum;
}
void Outdfs(int x,int s)//答案输出
{
if(x==n+1)
{
if(s==sum)
{
printf("Best Pairing %d\n",I);
++I;
for(int i=1;i<=n;++i)
{
printf("Supervisor %d with Employee %d\n",i,f[i]);
}
}
return;
}
for(int i=1;i<=n;++i)
{
if(!rv[i]&&a[x]+b[i]==point[x][i])
{
rv[i]=true;
f[x]=i;
Outdfs(x+1,s-point[x][i]);
rv[i]=false;
}
}
}
int main()
{
int T;
scanf("%d",&T);
for(int w=1;w<=T;++w)
{
scanf("%d",&n);
memset(point,0,sizeof(point));
int k;
for(int i=1;i<=n;++i)
{
for(int j=0;j<n;++j)
{
scanf("%d",&k);
point[k][i]-=j;//原题求最小 转化为求最大
}
}
for(int i=1;i<=n;++i)
{
for(int j=0;j<n;++j)
{
scanf("%d",&k);
point[i][k]-=j;
}
}
printf("Data Set %d, Best average difference: %.6f\n",w,1.0*KM()/2.0/n);
memset(rv,false,sizeof(rv));
I=1;
Outdfs(1,0);
printf("\n");
}
return 0;
}