• 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]));
    }
    
  • 相关阅读:
    一款开源免费跨浏览器的视频播放器--videojs使用介绍(转)
    forward内部跳转 和redirect重定向跳转的区别
    心理学--斯纳金
    心理学--大脑
    心理学--普及
    经济--国债,外汇
    经济--公积金
    经济--技术分析
    经济--分级基金3
    经济--分级基金
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/13492826.html
Copyright © 2020-2023  润新知