Description
在一个N行M列的二维网格里,有些格子是空地(用字符‘.’表示),有些格子是障碍物(用字符‘#’表示)。每个空地格子都有一只虫子,虫子不会移动。FJ打算用最少的炸弹把所有的虫子消灭。FJ每次可以选择在一个空地格子(不妨假设是格子a)放置一个炸弹,这个炸弹爆炸后,格子a的虫子会被消灭,假设有另一个空地格子b,如果空地格子b同时满足如下两个条件,那么空地b格子的虫子也会被该炸弹消灭:
1.格子a和格子b在同一行或者在同一列。
2.格子a和格子b之间没有障碍物格子。
有趣的是,任意两个不同的空地格子都有且只有一条由空地格子构成的路径,即所有空地格子构成一棵树形结构。注意:炸弹并不能毁灭障碍物!
Input
第一行,两个整数,n和m。1 <= n, m<=50。
接下来是n行m列的二维网格。
Output
输出最少的炸弹数。
Sample Input
输入1:
3 4
...
..##
.##
输入2:
3 7
.#.#.#.
.......
.#.#.#.
Sample Output
输出1:
2
输出2:
4
Data Constraint
30%的数据满足空地格子数量小于20
Hint
样例解释1:
.B.
.B##
.##
样例解释2:
.#.#.#.
B.B.B.B
.#.#.#.
字符B表示放置炸弹
赛时
看到树形结构就开始往DP上想。
画了半天的式子,发现状态特别乱,主要是很难处理其炸弹延伸出去的情况。
然后想了个水法,由于只能放在拐角处,于是很“聪明”地打了个暴力,加了几个剪枝。其实一点也不聪明。
题解
%%%lyl大爷2年前就切了,而且方法及其巧妙。
此题难点主要在于状态设定,lyl大爷的状态就提供了个很好的思路。
由于是树形结构,所以考虑树形dp。先把树建出来,然后类似于重链剖分一样把树按照横纵方向确定一条条链。
现在就可以考虑转移状态:
- 设(f[x][0,1,2])表示:(注意,下面“所在的链”只包括x节点下面的部分,它上面的部分不考虑)
- 0:当前x节点所在的链未放置炸弹,可能有某些位置并未被炸弹炸到过。
- 1:当前x节点所在的链未放置炸弹,一定没有位置未被炸弹炸到过。
- 2:当前x节点所在的链放置了炸弹。
那么我们就可以愉快转移了。
只需要分类讨论当前节点有多少个儿子的情况,讨论的情况数也多,可以自己捣鼓。当然也可以看标 (前提是你看得懂)
代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=10010;
const int inf=1000000;
int fx[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int n,m,ma[55][55],cnt,id[55][55];
int tot,nex[maxn*2],las[maxn*2],tov[maxn*2],ki[maxn*2];
int f[maxn][3],siz[maxn];
char s[55];
bool bz[maxn];
int min1(int x,int y,int z)
{
return min(x,min(y,z));
}
void insert(int x,int y,int z)
{
tot++;
tov[tot]=y;
nex[tot]=las[x];
las[x]=tot;
ki[tot]=z;
}
void dfs(int x,int ff,int fw)
{
bz[x]=true;
int son=0,son1=0,son2=0;
for (int i=las[x];i;i=nex[i])
{
if (tov[i]!=ff && !bz[tov[i]])
{
dfs(tov[i],x,ki[i]);
siz[x]++;
if (ki[i]==fw)
{
son=tov[i];
}
else
{
if (son1==0) son1=tov[i];
else son2=tov[i];
}
}
}
if (siz[x]==0)
{
f[x][0]=0;
f[x][1]=inf;
f[x][2]=1;
}
else
if (siz[x]==1)
{
if (son!=0)
{
f[x][0]=min(f[son][0],f[son][1]);
f[x][1]=inf;
f[x][2]=min(f[son][2],f[x][0]+1);
}
else
{
f[x][0]=f[son1][1];
f[x][1]=f[son1][2];
f[x][2]=min1(f[son1][2],f[son1][1],f[son1][0])+1;
}
}
else
if (siz[x]==2)
{
if (son!=0)
{
f[x][0]=min(f[son1][1],f[son1][2])+f[son][0];
f[x][1]=f[son1][2]+f[son][1];
f[x][2]=min(min1(f[son1][0],f[son1][1],f[son1][2])+min1(f[son][0],f[son][1],f[son][2])+1,
min(f[son1][1],f[son1][2])+f[son][2]);
}
else
{
f[x][0]=f[son1][1]+f[son2][1];
f[x][1]=min(f[son1][2]+min1(f[son2][2],f[son2][1],f[son2][0]),f[son2][2]+min1(f[son1][2],f[son1][1],f[son1][0]));
f[x][2]=1+min1(f[son2][2],f[son2][1],f[son2][0])+min1(f[son1][2],f[son1][1],f[son1][0]);
}
}
else
if (siz[x]==3)
{
f[x][0]=min(min1(f[son1][0],f[son1][1],f[son1][2])+f[son2][2],min1(f[son2][0],f[son2][1],f[son2][2])+f[son1][2])+f[son][0];
f[x][1]=min(min1(f[son1][0],f[son1][1],f[son1][2])+f[son2][2],min1(f[son2][0],f[son2][1],f[son2][2])+f[son1][2])+f[son][1];
f[x][2]=min(min1(f[son1][0],f[son1][1],f[son1][2])+min1(f[son2][0],f[son2][1],f[son2][2])+min1(f[son][0],f[son][1],f[son][2])+1,
min(min1(f[son1][0],f[son1][1],f[son1][2])+f[son2][2],min1(f[son2][0],f[son2][1],f[son2][2])+f[son1][2])+f[son][2]);
}
}
int main()
{
scanf("%d%d",&n,&m);
cnt=0;
for (int i=1;i<=n;i++)
{
scanf("%s",s+1);
for (int j=1;j<=m;j++)
{
if (s[j]=='.')
{
ma[i][j]=1;
id[i][j]=++cnt;
}
}
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
if (ma[i][j]==1)
{
for (int k=0;k<=3;k++)
{
int x=i+fx[k][0];
int y=j+fx[k][1];
if (x>0 && x<=n && y>0 && y<=m)
{
if (ma[x][y]==1)
{
insert(id[i][j],id[x][y],(k+1)/2);
insert(id[x][y],id[i][j],(k+1)/2);
}
}
}
}
}
}
dfs(1,0,1);
printf("%d
",min(f[1][1],f[1][2]));
}