题目
解题思路
注意到图的特点是一棵树,那我们就把这棵树建出来,如果炸掉某个点(p)使点(i)也消失,则点i在树上为点(p)的父亲/兄弟/儿子,如样例:
'#123'
'45##'
'#6##'
2
/
1 3
/
5 6
/
4
具体做法可以先选一个只能单向(上下、左右)扩展的点,然后搜索一下。接着树形(dp),(f_{x,0/1/2})分别表示 (0:x)不放炸弹且x没被炸;(1:x)不放炸弹但x已被炸;(2:x)放炸弹 的最小炸弹数。方程:
(f_{x,0}=sum f_{son,1})
(f_{x,1}=min( f_{p,2} + sum min(f_{son,0},f_{son,1},f_{son,2}) ) 满足(p∈x的儿子 son≠p))
(f_{x,2}=sum min(f_{son,0},f_{son,1},f_{son,2}) + 1)
求(f_{x,1})时用个小优化就可以(O(n))了
Code
#include<cstdio>
#include<algorithm>
using namespace std;
int a[55][55],n,m,tot = 0,f[2505][3],h[2505];
int dx[4] = {1,0,-1,0},dy[4] = {0,1,0,-1};
char s[55];
struct nd{
int x,y,c;
}q[2505];
struct edge{
int to,nxt;
}e[5000];
void add(int x,int y)
{
e[++tot] = (edge){y,h[x]};
h[x] = tot;
}
void bfs(int x,int y)
{
int head = 0,tail = 0,l = 1;
q[++tail].x = x;
q[tail].y = y;
q[tail].c = l;
a[x][y] = 0;
while (head < tail)
{
head++;
int xx = q[head].x,yy = q[head].y;
while (a[xx - 1][yy])
xx--,a[xx][yy] = 0,q[++tail].x = xx,q[tail].y = yy,l++,
add(q[head].c,l),add(l,q[head].c),q[tail].c = l;
xx = q[head].x;
while (a[xx + 1][yy])
xx++,a[xx][yy] = 0,q[++tail].x = xx,q[tail].y = yy,l++,
add(q[head].c,l),add(l,q[head].c),q[tail].c = l;
xx = q[head].x;
while (a[xx][yy - 1])
yy--,a[xx][yy] = 0,q[++tail].x = xx,q[tail].y = yy,l++,
add(q[head].c,l),add(l,q[head].c),q[tail].c = l;
yy = q[head].y;
while (a[xx][yy + 1])
yy++,a[xx][yy] = 0,q[++tail].x = xx,q[tail].y = yy,l++,
add(q[head].c,l),add(l,q[head].c),q[tail].c = l;
}
}
void dfs(int u,int fa)
{
int ms = 2147483647;
f[u][2] = 1;
for (int i = h[u]; i; i = e[i].nxt)
{
int v = e[i].to;
if (v == fa) continue;
dfs(v,u);
f[u][0] += f[v][1];
f[u][1] += min(f[v][0],min(f[v][1],f[v][2]));
ms = min(ms,f[v][2] - min(f[v][0],min(f[v][1],f[v][2])));
f[u][2] += min(f[v][0],min(f[v][1],f[v][2]));
}
if (ms != 2147483647) f[u][1] += ms;
else f[u][1] = 1e7;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++)
{
scanf("%s",s + 1);
for (int j = 1; j <= m; j++)
if (s[j] == '.') a[i][j] = 1;
}
for (int i = 1; i <= n; i++)
{
int flag = 0;
for (int j = 1; j <= m; j++)
{
if (!a[i][j]) continue;
int flag1 = 0,flag2 = 0;
if (a[i + 1][j] || a[i - 1][j]) flag1 = 1;
if (a[i][j + 1] || a[i][j - 1]) flag2 = 1;
if (flag1 == 1 && flag2 == 1);
else bfs(i,j),flag = 1;
if (flag) break;
}
if (flag) break;
}
dfs(1,1);
printf("%d",min(f[1][1],f[1][2]));
}