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