学习了下二分图多重匹配,感觉挺不错的,当点数比较少的时候,就不用转化成最大流来做了,代码复杂度较最大流低。
其实学过匈牙利二分图最大匹配,学这个也很容易,所谓多重匹配,就是Y集合某点可以和X集合多点同时匹配,也就是说Y集合点度不再是一,问X集合的最大匹配是多少。
对于X集合点u要和Y集合点v匹配,如果v点度未满,则直接uv匹配,否则,对v点的每一个父节点增广。
这题比较容易想到二分,二分最小的range值,对每一个range,枚举起点,然后用匹配判断是否可行,如果有一个起点可行,则此range值可以满足要求,继续二分迭代。
代码
1 /*二分搜索 + 多重二分匹配*/
2 #include<stdio.h>
3 #include<string.h>
4 #define NN 1004
5 #define BB 24
6
7 int N, B, maxRange;
8 int rank[NN][BB]; // rank[i][j] == k,表示barn j 在cow i 心中的rank 为 k
9 int cap[BB];
10 int fat[BB][NN];
11 int vis[BB];
12 int can(int base, int t){
13 int i, j;
14 for (i = 1; i <= B; i++){
15 if (rank[t][i] >= base && rank[t][i] <= maxRange + base - 1){
16 if (!vis[i]){
17 vis[i] = 1;
18 for (j = 1; j <= cap[i]; j++){// 如果度未满,直接匹配
19 if (fat[i][j] == -1){
20 fat[i][j] = t;
21 return 1;
22 }
23 }
24 for (j = 1; j <= cap[i]; j++){// 如果已满,对i节点的第j个父节点增广
25 if (can(base, fat[i][j])){
26 fat[i][j] = t;
27 return 1;
28 }
29 }
30 }
31 }
32 }
33 return 0;
34 }
35 /*二分搜索*/
36 int Assignment(){
37 int i, j, ans;
38 for (j = 1; j <= B - maxRange + 1; j++){ // 枚举所有range为maxRange的起点
39 ans = 0;
40 memset(fat, -1, sizeof(fat));
41 for (i = 1; i <= N; i++){
42 memset(vis, 0, sizeof(vis));
43 if (can(j, i)) ans++;
44 }
45 if (ans == N) return 1;
46 }
47 return 0;
48 }
49 int Binary(){
50 int low, hig, mid, ans;
51 ans = -1;
52 low = 1;
53 hig = B;
54 do{
55 mid = (low + hig) >> 1;
56 maxRange = mid;
57 if (Assignment()){
58 ans = mid;
59 hig = mid - 1;
60 }else low = mid + 1;
61 }while(low <= hig);
62 return ans;
63 }
64 int main()
65 {
66 int i, j, d;
67 scanf("%d%d", &N, &B);
68 for (i = 1; i <= N; i++){
69 for (j = 1; j <= B; j++){
70 scanf("%d", &d);
71 rank[i][d] = j;
72 }
73 }
74 for (i = 1; i <= B; i++){
75 scanf("%d", &cap[i]);
76 }
77
78 printf("%d\n", Binary());
79 return 0;
80 }
81