P6062 Muddy Fields G
形式化题意:
有一个 \(R\times C\) 的矩形,其中 \(1 \le R,C \le 50\),矩形包含两种字符 * 和 .。你需要使用尽量少的 \(1\times x\) 的木板,从而覆盖所有 * 而不含有 .(一个 * 上可以重复叠木板)。
做法:
我们首先有一个性质就是最优情况下木板的左右两端必然是没有 * 的,因为取 * 肯定比不取更优。
所以所有可能的木板就是横着的每一行的连续一段和竖着的每一列的连续一段。
那么我们对于每个格子它就有两种可以用于覆盖的木板。这个时候我们考虑建模二分图匹配。我们把横着的木板作为左边的点,竖着的木板作为右边的点,每一个 * 就是一条边,连接可以覆盖它的两个木板。然后求最小边覆盖,也就是求最大二分图匹配。
Code:(这里的二分图匹配用的网络流,因为匈牙利还没学)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 10010, INF = 1e9;
struct node {
int lst, v, w;
} e[N << 1];
int n, m, S, T, tot = -1, h[N], cur[N], d[N];
int in[N], out[N];
bool vis[N];
int sum[N], r[N];
vector<int> vv[25];
void add(int u, int v, int w){
e[++tot] = {h[u], v, w}; h[u] = tot;
e[++tot] = {h[v], u, 0}; h[v] = tot;
}
bool bfs() {
memset(d, -1, sizeof d);
queue<int> q;
q.push(S); d[S] = 0;
cur[S] = h[S];
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = h[u]; ~i; i = e[i].lst) {
int v = e[i].v;
if(d[v] == -1 && e[i].w > 0) {
d[v] = d[u] + 1; cur[v] = h[v];
q.push(v);
}
}
}
return d[T] != -1;
}
int dfs(int u, int in) {
if(u == T || !in) return in;
int out = 0;
for(int &i = cur[u]; ~i; i = e[i].lst) {
int v = e[i].v;
if(d[v] == d[u] + 1 && e[i].w > 0) {
int f = dfs(v, min(in, e[i].w));
if(!f) continue;
e[i].w -= f; e[i ^ 1].w += f;
in -= f; out += f;
if(!in) break;
}
}
if(!out) d[u] = -1;
return out;
}
int Dinic() {
int ans = 0;
while (bfs()) ans += dfs(S, INF);
return ans;
}
char c[55][55];
int p1[55][55], p2[55][55];
int main(){
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
int cnt1 = 0, cnt2 = 0;
for(int i = 1; i <= n; i++) {
scanf("%s", c[i] + 1);
int sum = 0;
for(int j = 1; j <= m; j++) {
if(c[i][j] == '*') sum++, p1[i][j] = cnt1 + 1;
else if(c[i][j - 1] == '*') cnt1++, sum = 0;
}
if(sum) cnt1++, sum = 0;
}
for(int j = 1; j <= m; j++) {
int sum = 0;
for(int i = 1; i <= n; i++) {
if(c[i][j] == '*') sum++, p2[i][j] = cnt2 + 1;
else if(c[i - 1][j] == '*') cnt2++, sum = 0;
}
if(sum) cnt2++, sum = 0;
}
S = 0; T = cnt1 + cnt2 + 1;
for(int i = 1; i <= cnt1; i++) add(S, i, 1);
for(int i = 1; i <= cnt2; i++) add(i + cnt1, T, 1);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(c[i][j] == '*') {
add(p1[i][j], cnt1 + p2[i][j], 1);
}
}
}
printf("%d\n", Dinic());
return 0;
}

浙公网安备 33010602011771号