jzoj3252. 【GDOI三校联考】炸弹

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]));
}
原文地址:https://www.cnblogs.com/RainbowCrown/p/13492826.html