炸弹

题目



解题思路

注意到图的特点是一棵树,那我们就把这棵树建出来,如果炸掉某个点(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]));
}
原文地址:https://www.cnblogs.com/nibabadeboke/p/13472210.html