Problem
-
给出一个网格图,由010101构成,其中000相连,构成一颗树.
-
可以放炸弹,一个炸弹可以覆盖连续的一行000或一列000.
-
求放至少多少个炸弹,使得每个000都会被至少111个炸弹覆盖。
Data constraint
- n,m≤50n,m\le 50n,m≤50
Solution
-
数据范围很小,加起来就250025002500个点,很容易让人误入歧途.
-
正解实质上是DP
-
以从左到右,从上到下的顺序找到第一个000,作为根,依次走下去,形成一颗树.
-
考虑每个格子(x,y)(x,y)(x,y)的状态,不妨假设其父亲格点是(x−1,y)(x-1,y)(x−1,y):
- ①:直接在(x,y)(x,y)(x,y)这个点放了一个炸弹.
- ②:由(x,y−1),(x,y+1)(x,y-1),(x,y+1)(x,y−1),(x,y+1)转移过来,即xxx行放了炸弹,且能影响到(x,y)(x,y)(x,y).
- ③:由(x+1,y)(x+1,y)(x+1,y)转移过来,即在第yyy列放了炸弹,且能影响到(x,y)(x,y)(x,y).
-
为何要把第二、三种分开讨论?
-
显然,这是因为(x+1,y)(x+1,y)(x+1,y)的影响不仅仅是(x,y)(x,y)(x,y),可能会影响到它父亲,父亲的父亲……
-
所以,我们首先要把一个点的状态给分析清楚,即可能有怎样的决策影响.
-
分析好状态,我们不难想到这样的状态设计:
- f[i][0]f[i][0]f[i][0]:表示以iii为根的子树,没放炸弹,还剩一条与父亲向同向的子树链没有炸.
- f[i][1]f[i][1]f[i][1]:表示以iii为根的子树,子树炸完了,但没有炸出去,即父亲向的方向上没有放炸弹.
- f[i][2]f[i][2]f[i][2]:表示以iii为根的子树,子树炸完了,且炸出去了,即父亲向的方向上有炸弹.
-
如此一来,我们便可以很方便的讨论转移了.
-
具体转移分成四类,即有0,1,2,30,1,2,30,1,2,3个儿子时的情况.
-
这里以333个儿子为例,其他是类似的,且333个儿子也是最复杂的。
不妨设目前转移点编号为k
其中p[1],p[2],p[3]为k的3个儿子,且p[1]在父亲向(上下)的方向上,p[2],p[3]在k点的左右方向上.
那么必定有:
f[k][0] = min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2],
Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2]
) + f[p[1]][0];
//f[p[1]][0]是必须要转移的,因为当前点没放炸弹,如果要延续,则儿子必须连续
//因为当前点没有放炸弹,所以“左右”方向上必定有一个是炸出去了的,也就是f[p[2]][2]或f[p[3]][2]必选一个
//另外一个则可以随便选,所以是取3个min值.
f[k][1] = min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2],
Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2]
) + f[p[1]][1];
//f[p[1]][1]是必须要转移的,因为当前点也不能放炸弹.
//其余与上面一种情况完全类似.
f[k][2] = min( (1 + Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) +
Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) +
Min(f[p[3]][0], f[p[3]][1], f[p[3]][2])),
0 + f[p[1]][2] +
min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2],
Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2])
);
}
//这里的转移就分两种情况讨论了,即当前点放炸弹还是不放.
//看一看,应该还是很好理解的.
-
对于其余的三种情况,其实也并不简单多少,因为还要继续分类讨论,即有没有上下的这种父亲向的儿子.
-
总之,就是这样的一个DP:
#include <cstdio>
#include <iostream>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define Min(a, b, c) ((a) < min(b, c) ? a : min(b, c))
#define num(a, b) ((a - 1) * m + b)
const int N = 51, M = 10 * N * N, dx[4] = {0, - 1, 1, 0}, dy[4] = {- 1, 0, 0, 1}, Impossible = 1e9;
using namespace std;
int n, m, tot, Sx, Sy, f[M][3]; char ch[N][N];
int tov[M], nex[M], las[M], Spe[M], Fa[M];
void ins(int x, int y, int z) {
tov[++ tot] = y, nex[tot] = las[x], las[x] = tot, Spe[tot] = z;
}
void Dfs(int k, int La) {
int SonTotal = 0, p[4]; p[0] = p[1] = p[2] = p[3] = 0;
for (int x = las[k] ; x ; x = nex[x])
if (tov[x] ^ La)
Fa[tov[x]] = Spe[x], Dfs(tov[x], k), p[++ SonTotal] = tov[x];
if (SonTotal == 0) {
f[k][0] = 0;
f[k][1] = Impossible;
f[k][2] = 1;
}
if (SonTotal == 1) {
if (Fa[p[1]] == Fa[k]) {
f[k][0] = min(f[p[1]][0], f[p[1]][1]);
f[k][1] = Impossible;
f[k][2] = min(min(f[p[1]][0], f[p[1]][1]) + 1, f[p[1]][2]);
}
else {
f[k][0] = f[p[1]][1];
f[k][1] = f[p[1]][2];
f[k][2] = Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) + 1;
}
}
if (SonTotal == 2) {
if (Fa[p[1]] == Fa[k] || Fa[p[2]] == Fa[k]) {
if (Fa[p[2]] == Fa[k]) swap(p[1], p[2]);
f[k][0] = min(f[p[2]][1], f[p[2]][2]) + f[p[1]][0];
f[k][1] = f[p[2]][2] + f[p[1]][1];
f[k][2] = min( (1 + Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) + Min(f[p[2]][0], f[p[2]][1], f[p[2]][2])),
(0 + f[p[1]][2] + min(f[p[2]][1], f[p[2]][2])) );
}
else
{
f[k][0] = f[p[1]][1] + f[p[2]][1];
f[k][1] = min( Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) + f[p[2]][2], Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[1]][2]);
f[k][2] = 1 + Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) + Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]);
}
}
if (SonTotal == 3) {
if (Fa[p[2]] == Fa[k]) swap(p[1], p[2]);
if (Fa[p[3]] == Fa[k]) swap(p[1], p[3]);
f[k][0] = min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2], Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2]) + f[p[1]][0];
f[k][1] = min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2], Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2]) + f[p[1]][1];
f[k][2] = min( (1 + Min(f[p[1]][0], f[p[1]][1], f[p[1]][2]) + Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + Min(f[p[3]][0], f[p[3]][1], f[p[3]][2])),
0 + f[p[1]][2] + min( Min(f[p[2]][0], f[p[2]][1], f[p[2]][2]) + f[p[3]][2], Min(f[p[3]][0], f[p[3]][1], f[p[3]][2]) + f[p[2]][2]) );
}
}
int main() {
scanf("%d%d", &n, &m);
F(i, 1, n) scanf("%s", ch[i] + 1);
F(i, 1, n)
F(j, 1, m)
if (ch[i][j] == '.') {
F(k, 0, 3) {
int x = i + dx[k], y = j + dy[k];
if (x && y && x <= n && y <= m && ch[x][y] == '.')
ins( num(x,y), num(i,j), k );
}
Sx = Sx == 0 ? i : Sx, Sy = Sy == 0 ? j : Sy;
}
Dfs(num(Sx, Sy), 0);
printf("%d\n", min(f[num(Sx, Sy)][1], f[num(Sx, Sy)][2]));
}