学习了下二分图多重匹配,感觉挺不错的,当点数比较少的时候,就不用转化成最大流来做了,代码复杂度较最大流低。
其实学过匈牙利二分图最大匹配,学这个也很容易,所谓多重匹配,就是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