PS:速报,模拟赛玄学 T1 没挂。
首先木板一定是极长的,根据贪心这肯定最优。
然后就是选择什么木板的问题,考虑一个经典套路:网格建图。
对于每一个点,把横向覆盖和竖向覆盖它的木板连边,然后得到了一个二分图。
放一个木板相当于连接两个点,需要覆盖所有点,所以要求二分图的最小点覆盖。
由于最小点覆盖等于最大匹配,所以建图跑一遍匈牙利即可。
没有想到我睡醒起来直接码代码,调完编译没测样例交上去直接过了。
#include <bits/stdc++.h>
using namespace std;
const int N = 55, N1 = N * N, M1 = N1 << 1;
int n, m;
char ch[N][N];
int c1[N][N], c2[N][N], tot1, tot2;
int edge[N1][N1];
int h[N1], e[M1], ne[M1], idx = 0;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int ans = 0;
int match[N1];
bool st[N1];
int dfs(int u) {
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (st[v]) continue;
st[v] = 1;
if (!match[v] || dfs(match[v])) {
match[v] = u;
return 1;
}
}
return 0;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf(" %s", ch[i] + 1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (ch[i][j] == '*' && ch[i][j - 1] != '*') tot1++;
if (ch[i][j] == '*') c1[i][j] = tot1;
}
for (int j = 1; j <= m; j++)
for (int i = 1; i <= n; i++) {
if (ch[i][j] == '*' && ch[i - 1][j] != '*') tot2++;
if (ch[i][j] == '*') c2[i][j] = tot2;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (ch[i][j] != '*') continue;
edge[c1[i][j]][c2[i][j]] = 1;
}
for (int i = 1; i <= max(tot1, tot2); i++) h[i] = -1;
for (int i = 1; i <= tot1; i++)
for (int j = 1; j <= tot2; j++)
if (edge[i][j]) add(i, j);
for (int i = 1; i <= tot1; i++) {
for (int j = 1; j <= tot2; j++) st[j] = 0;
if (dfs(i)) ans++;
}
printf("%d\n", ans);
return 0;
}