题目链接:P6062 [USACO05JAN]Muddy Fields G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
此题重点在二分图的建图。
考虑放置木板的决策,由于可以重复覆盖,所以对于两个可以选择的区间 a,b,若 b 被 a 覆盖,那么选择 a 一定优于选择 b。
解释如图:泥地为黄色,草地为绿色
显然,木板 A 一定比木板 B 更优,因为橙色部分可以重复覆盖。
由此易得所有的木板两头都是没有泥地的:若有,则增长此木板一定比原方案更优。
得到这个结论,我们可以先预处理出所有可能放置木板的位置,也就是在每行每列连续的泥地的位置。如图所示:所有木板可能放置的位置是下图的十个。
对于每一个点,要么被横着覆盖,要么被竖着覆盖。举例来讲,(3,3)在行上会被横木板 3 覆盖,在列上会被竖木板 4 覆盖。而我们要让所有的点都被覆盖,也就是需要满足下图所示的所有条件:
我们需要选出最少的木板满足这样的若干个约束条件。我们把每一个泥地看成一条边,左端点为其对应的横木板,右端点为其对应的竖木板。 显然这是一个二分图,左边全部为横木板,右边全部为竖木板。
考虑我们放置一个木板,相当于满足所有包含它的约束条件,也就是将其所连的边进行染色。 而对于每一个泥地(也就是边),如果它被染色,就证明这个格子的约束条件已被满足。
所以这个问题转化为:一次操作可以选择一个点,将所连的边染色,目的是让所有边被染色。
即为二分图最小点覆盖。
而最小点覆盖等于最大匹配,故建图跑匈牙利即可。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int N = 55 * 55; int n, m; bool st[N]; int match[N]; bool con[N][N]; char str[N][N]; int row[N][N], col[N][N]; int row_cnt, col_cnt; bool find(int a) { for (int b = 1; b <= col_cnt; b ++ ) { if (!con[a][b]) continue; if (st[b]) continue; st[b] = true; if (match[b] == -1 || find(match[b])) { match[b] = a; return true; } } return false; } int main() { cin >> n >> m; memset(match, -1, sizeof match); for (int i = 1; i <= n; i ++ ) cin >> str[i] + 1; for (int i = 1; i <= n; i ++ )//按行枚举找木板数 for (int j = 1; j <= m; j ++ ) if (str[i][j] == '*') if (j == 1 || str[i][j - 1] != '*') row[i][j] = ++ row_cnt ; else row[i][j] = row_cnt; for (int i = 1; i <= m; i ++ )//按列枚举找木板数 for (int j = 1; j <= n; j ++ ) if (str[j][i] == '*') if (j == 1 || str[j - 1][i] != '*') col[j][i] = ++ col_cnt; else col[j][i] = col_cnt; for (int i = 1; i <= n; i ++ )//建图 for (int j = 1; j <= m; j ++ ) con[row[i][j]][col[i][j]] = true; int res = 0; for (int i = 1; i <= row_cnt; i ++ )//最小点覆盖 = 最大匹配数 { memset(st, false, sizeof st); if (find(i)) res ++ ; } cout << res << endl; return 0; }