pku 2400 Supervisor, Supervisee KM求最小权匹配+DFS回溯解集
http://poj.org/problem?id=2400
题意:
有n个管理员需要雇佣n个工作人员。 每个管理员对每个工作人员的评价不同,评价值(score)从0-n-1,0代表评价最高,n-1代表评价最低,(这样处理用KMq求解时才能出现0)同样,每个工作人员对每个管理员也有不同 的评价,评价值也是从0-n-1,0代表评价值最高,n-1代表最低。问n个管理员怎样选择n个工作人员可以使的每个人的平均评价值最小。即总的评价值 /(2*n)最小。如果存在多种最佳方案,则按照字典序输出每一种情况。
思路:
我们把N个管理员与N个员工分成两个点集,X,Y.管理员X[i]与员工Y[j]总的评价值为X[i]对Y[j]的评价值+Y[j]对X[i]的评价值。然后建图求最小权匹配即可。KM求完之后得到完备匹配,由于所有可能的匹配肯定在所得到的完备匹配的相等子图上,在其上dfs所有可能结果即可。
#include <cstdio> #include <cstring> #include <iostream> #define maxn 17 using namespace std; const int inf = 99999999; int w[maxn][maxn],link[maxn],match[maxn]; int lx[maxn],ly[maxn]; bool vtx[maxn],vty[maxn],vt[maxn]; int slack[maxn]; int n,cnt; bool dfs(int i) { int j; vtx[i] = true; for (j = 1; j <= n; ++j) { if (vty[j]) continue; int tmp = lx[i] + ly[j] - w[i][j]; if (tmp == 0) { vty[j] = true; if (link[j] == -1 || dfs(link[j])) { link[j] = i; return true; } } else slack[j] = min(tmp,slack[j]); } return false; } int KM() { int i,j; for (i = 1; i <= n; ++i) { for (j = 1,lx[i] = -inf; j <= n; ++j) { lx[i] = max(lx[i],w[i][j]); } } for (i = 1; i <= n; ++i) { ly[i] = 0; link[i] = match[i] = -1; vt[i] = false; } for (i = 1; i <= n; ++i) { for (j = 1; j <= n; ++j) slack[j] = inf; while (1) { for (j = 1;j <= n; ++j) vtx[j] = vty[j] = false; if (dfs(i)) break; int d = inf; for (j = 1; j <= n; ++j) { if (!vty[j] && d > slack[j]) d = slack[j]; } for (j = 1; j <= n; ++j) if (vtx[j]) lx[j] -= d; for (j = 1; j <= n; ++j) if (vty[j]) ly[j] += d; else slack[j] -= d; } } int sum = 0; for (i = 1; i <= n; ++i) if (link[i] > -1) sum -= w[link[i]][i]; return sum; } void DfsRes(int i) { if (i > n) { printf("Best Pairing %d\n",cnt++); for (i = 1; i <= n; ++i) { printf("Supervisor %d with Employee %d\n",i,match[i]); } } for (int j = 1; j <= n; ++j) { if (lx[i] + ly[j] == w[i][j] && !vt[j]) { vt[j] = true; match[i] = j; DfsRes(i + 1); vt[j] = false; } } } int main() { int i,j,t,k; int cas = 1; scanf("%d",&t); while (t--) { scanf("%d",&n); memset(w,0,sizeof(w)); /* 题目给定的关系矩阵给反了所以建图有点纠结了; 意思不变。 */ for (i = 1; i <= n; ++i) { for (j = 0; j < n; ++j) { scanf("%d",&k); w[k][i] -= j; } } for (i = 1; i <= n; ++i) { for (j = 0; j < n; ++j) { scanf("%d",&k); w[i][k] -= j; } } printf("Data Set %d, Best average difference: %.6lf\n",cas++,KM()/(2.0*n)); /* 在完备匹配的相等子图上搜索所有结果; */ cnt = 1; DfsRes(1); printf("\n"); } return 0; }