POJ 2400 KM+建图

POJ 2400

题意

  • n个部门n个员工,每个部门雇佣一个员工,部门希望雇佣每个员工的希望值不同,员工希望被雇佣的部门的希望值也不同, 求使得所有人希望值之和最大,输出最优匹配结果,若有多种,输出所有

思路(借鉴大佬)

  • 题目数据与描述不一样,先给出的每个员工的对部门的希望值,然后才是每个部门的首选项,

  • 将左部分作为部门,右部分作为员工,边权为各自希望值之和,KM求出最大的希望值之和

  • 要求输出的 最好平均差(通过样例理解)就是 (每个部门和员工都是最好匹配的权值和- 最大权值匹配和)/总个数

  • 得到最好平均差还要dfs搜二分图,把搜到的匹配和KM求出的ans比较, 相等则说明是一组最大权值匹配


  • 为什么w[][]还是负值? 不是求最大希望和吗? 因为希望最大的在最开始,那么换成w[to][i] = n-j等价于换成负数(体会一下)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
const int inf = 1<<30;
const int maxn = 25;
int w[maxn][maxn],la[maxn],lb[maxn],match[maxn];
int n,m,delta,cnt,ans;
bool va[maxn],vb[maxn];

int dfs(int u) {
va[u] = 1;
for (int i=1; i<=n; ++i) {
if (!vb[i]) {
if (la[u]+lb[i]-w[u][i] == 0) {
vb[i] = 1;
if (match[i]==-1 || dfs(match[i])) {
match[i] = u;
return 1;
}
} else delta = min(delta,la[u]+lb[i]-w[u][i]);
}
}
return 0;
}

int KM() {
for (int i=0; i<maxn; ++i) la[i] = -inf;
memset(lb,0,sizeof lb);
memset(match,-1,sizeof match);
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j)
la[i] = max(la[i],w[i][j]);
for (int i=1; i<=n; ++i) {
while (1) {
memset(va,0,sizeof va);
memset(vb,0,sizeof vb);
delta = inf;
if (dfs(i)) break;
for (int j=1; j<=n; ++j) {
if (va[j]) la[j] -= delta;
if (vb[j]) lb[j] += delta;
}
}
}
ans = 0;
for (int i=1; i<=n; ++i)
if (match[i]) ans += w[match[i]][i];
return -ans;
}

void dfs(int u,int sum) {
if (sum < ans) return ;//剪枝,因为边权都为负数,当前和都小于ans,再加上负数不可能达到ans的值(这组全排列不是KM的匹配)
if (u > n) {// 前n位搜出来了,判断搜出来的边权和是否=ans
if (sum != ans) return ;
printf("Best Pairing %d\n",cnt++);
for (int i=1; i<=n; ++i)
printf("Supervisor %d with Employee %d\n",i,match[i]);
} else {
for (int i=1; i<=n; ++i) {
if (!va[i]) {
va[i] = 1;
match[u] = i;
dfs(u+1,sum+w[u][i]);
va[i] = 0;
}
}
}

}

int main () {
freopen("1.in","r",stdin);
int times,case1=1; cin >> times;
while (times--) {
scanf("%d",&n);
memset(w,0,sizeof w);
int to;
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j) {
scanf("%d",&to);
w[to][i] -= (j-1);
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=n; ++j) {
scanf("%d",&to);
w[i][to] -= (j-1);
}
printf("Data Set %d, Best average difference: %.6lf\n",case1++,KM()/(2.0*n));
memset(va,0,sizeof va);
cnt = 1;
dfs(1,0);
printf("\n");
}
return 0;
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!