思路:
这题完全不懂,看了这份大牛的解题报告
http://hi.baidu.com/winterlegend/blog/item/2411981e8fd0ed6bf724e45e.html
发现吗的太牛逼了!
首先二分图匹配,正常情况下,都是单个单个的点这样匹配、
但是像这道题,右边的点一个可以匹配好几个左边的点。也是用匈牙利算法,不过要做少少修改。
枚举答案的时候,有两种方法:
一个是移动区间的方法。复杂度O(B)。注意,只移动区间右边的时候不用重新匹配,只需要接着上次没匹配完的继续匹配就行了
一个是二分法。复杂度 O(B lgB)
由于数据的问题,这两种方法时间相差无几。在 16ms 和 32ms 之间。
#include <stdio.h>
struct barn {
int link[1024], cnt, vis, cap;
};
struct cow {
int rank[32];
};
struct barn barns[32];
struct cow cows[1024];
int start, end, last_pos;
int N, B, tm;
int dfs(int c)
{
int i, j;
struct barn *b;
for (i = start; i <= end; i++) {
b = &barns[cows[c].rank[i]];
if (b->vis == tm)
continue;
b->vis = tm;
if (b->cnt < b->cap) {
b->link[b->cnt++] = c;
return 1;
}
for (j = 0; j < b->cap; j++)
if (dfs(b->link[j])) {
b->link[j] = c;
return 1;
}
}
return 0;
}
inline void init()
{
int i;
for (i = 1; i <= B; i++)
barns[i].cnt = 0;
last_pos = 1;
}
inline int match()
{
int i, j;
for (i = last_pos; i <= N; i++) {
tm++;
if (!dfs(i))
break;
}
last_pos = i;
return i > N;
}
int main()
{
int i, j, ans;
freopen("e:\\test\\in.txt", "r", stdin);
scanf("%d%d", &N, &B);
for (i = 1; i <= N; i++)
for (j = 1; j <= B; j++)
scanf("%d", &cows[i].rank[j]);
for (i = 1; i <= B; i++)
scanf("%d", &barns[i].cap);
#if 0
// O(B)
ans = B;
start = end = 1;
while (start <= B && end <= B) {
init();
while (end <= B && !match())
end++;
if (end - start + 1 < ans)
ans = end - start + 1;
start++;
}
#else
// O(B*lgB)
int l, r, m;
l = 1;
r = B;
while (l <= r) {
m = (l + r) / 2;
for (start = 1; start <= B - m + 1; start++) {
end = start + m - 1;
init();
if (match())
break;
}
if (start == B - m + 2) {
// failed
l = m + 1;
} else
r = m - 1;
}
ans = r + 1;
#endif
printf("%d\n", ans);
return 0;
}