AtCoder Regular Contest 112 D
Solution
不难发现,对于所有的格子,只要它是一个有效格子,那么它们就可以到达 ( 1 , 1 ) (1,1) (1,1) 这个格子。因此我们可以反过来看这个问题,如果所有从 ( 1 , 1 ) (1,1) (1,1) 出发的旅行者都能到达所有格子,那么这张图就是一个有效图。
因为有墙面的存在,我们有以下结论
- 1 − s t 1-st 1−st 行是有效的 ⟺ 1 − s t 1-st 1−st 列是有效的
- 1 − s t 1-st 1−st 行是有效的 ⟺ W − t h W-th W−th 列是有效的
- H − t h H-th H−th 行是有效的 ⟺ 1 − s t 1-st 1−st 列是有效的
- H − t h H-th H−th 行是有效的 ⟺ W − t h W-th W−th 列是有效的
- 1 − s t 1-st 1−st 行是有效的
此外,如果 ( r , c ) (r,c) (r,c) 这个点是地面,我们可以得到
- r r r t h th th 行和 c c c t h th th 列都是有效的
把这个点和井号的集合想成一个图,假如有一行是可行的,那么它对于的列也是可行的。 即,如果所有行均可行,则连接与该行相对应的所有顶点。 因此,使所有行均可行所需的最小更改数等于使所有顶点对应于这些行所需的最小边数。 通过从包含与行相对应的顶点的已连接部分的数量中减去1,可以找到此最小边数。 我们也可以类似地处理这些列。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define debug(a) cout << #a << " " << a << endl
const int maxn = 1e5 + 7;
const int N = 1500, M = N * 2;
const int inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
char g[N][N];
int p[maxn], t1[maxn], t2[maxn];
int find(int x) { //返回x的祖宗节点 + 路径压缩
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void unionfind(int x, int y) {
p[find(x)] = find(y);
}
int main() {
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// ios::sync_with_stdio(false);
int H, W;
cin >> H >> W;
for(int i = 1; i <= H + W; i++) p[i] = i;
for(int i = 1; i <= H; i++)
for(int j = 1; j <= W; j++) {
cin >> g[i][j];
if(g[i][j] == '#') unionfind(i, j + H);
}
unionfind(1, H + 1);//通过添加一个偏移量保证行列的不同
unionfind(1, H + W);
unionfind(H, H + 1);
unionfind(H, H + W);
int ans1 = 0, ans2 = 0;
for(int i = 1; i <= H; i++) t1[find(i)] = 1;
for(int i = H + 1; i <= H + W; i++) t2[find(i)] = 1;
for(int i = 1; i <= H + W; i++) ans1 += t1[i];
for(int i = 1; i <= H + W; i++) ans2 += t2[i];
cout << min(ans1, ans2) - 1;
return 0;
}