题目大意:在一个50*50的地图内,有些是空地,另外的是障碍物,空地是连成一棵树。若在某空地放置一个炸弹,那么在同一列和同一行且没有障碍物阻拦的格子都被炸到(类似于泡泡堂。。。)。询问至少需要多少个炸弹才能把所有的格子都炸到。
题目中很关键的一个字眼就所有的空地是一棵树,这促使我们往树形动态规划的方面去想。
构建一棵二叉树,左子树表示在同一行的空地,右子树表示同一列的空地。那么,若把某个节点炸掉,受到影响的将会是它的父亲和祖先,左孩子和左孩子的左孩子,左孩子的左孩子的左孩子,以及右孩子,右孩子的右孩子....。对每个点记录两个状态,一个是该节点被炸以后,有一个向左上的威力把父亲炸掉,以及有一个右上的威力把父亲炸掉。
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
using namespace std;
#define st first
#define nd second
#define pi 3.141592653589793238462643383
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
const int inf = 0x3f3f3f3f;
#define Rep(i, n) for (int i = 0, _n = n; i < _n; i ++)
#define Repp(i, n) for (int i = 1, _n = n; i <= _n; i ++)
#define For(i, a, b) for (int i = a, _b = b; i <= _b; i ++)
#define Forr(i, a, b) for (int i = a, _b = b; i >= _b; i --)
typedef long long ll;
typedef pair<int, int> pii;
#define maxn 57
#define maxp maxn*maxn
int n, m, sx, sy;
int dat[maxn][maxn];
struct Pointer{
Pointer *lc, *rc;
int lf, rf;
int get() {
return min(lf, rf);
}
}head[maxp], *nll = head;
void Init() {
scanf("%d%d\n", &n, &m);
nll -> lf = nll -> rf = 0;
int cnt = 0; char ch;
Repp(i, n) {
Repp(j, m) {
scanf("%c", &ch);
if (ch == '.') {
// 编号
dat[i][j] = ++ cnt;
head[cnt].lc = head[cnt].rc = nll;
if (cnt == 1) sx = i, sy = j;
}
}
scanf("\n");
}
}
void dfs(Pointer *x) {
if (x == nll) return;
dfs(x -> lc);
dfs(x -> rc);
if (x -> rc != nll)
x -> lf = x -> rc -> lf + x -> lc -> get();
else x -> lf = inf;
if (x -> lc != nll)
x -> rf = x -> lc -> rf + x -> rc -> get();
else x -> rf = inf;
// 其实这里可以多记录两个部分和,那么不用每次往下找一次
// 时间复杂度从 O(n ^ 3) 降到 O(n ^ 2)
int tmp = 1;
Pointer *p = x;
while (true) {
p = p -> lc;
if (p == nll) break;
tmp += p -> rc -> get();
}
p = x;
while (true) {
p = p -> rc;
if (p == nll) break;
tmp += p -> lc -> get();
}
x -> lf = min(x -> lf, tmp);
x -> rf = min(x -> rf, tmp);
}
pii que[maxp];
// 构建二叉树
void Graph() {
int quehead = 0, quetail = 0;
que[0].st = sx, que[0].nd = sy;
while (quehead <= quetail) {
pii now = que[quehead ++];
Rep(k, 4) {
pii to;
to.st = now.st + dx[k];
to.nd = now.nd + dy[k];
if (dat[to.st][to.nd] > 0) {
Pointer *p = &head[dat[now.st][now.nd]];
// 若已经有(左、右)孩子,则一直找(左、右)孩子的(左、右)孩子。
if (k < 2) {
while (p -> lc != nll) p = p -> lc;
p -> lc = &head[dat[to.st][to.nd]];
}
else {
while (p -> rc != nll) p = p -> rc;
p -> rc = &head[dat[to.st][to.nd]];
}
que[++ quetail] = to;
}
}
dat[now.st][now.nd] = 0;
}
}
int main() {
freopen("bomb.in", "r", stdin);
freopen("bomb.out", "w", stdout);
Init();
Graph();
dfs(&head[1]);
printf("%d\n", min(head[1].lf, head[1].rf));
return 0;
}