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; }