ACM: FZU 2150 Fire Game - DFS+BFS+枝剪 或者 纯BFS+枝剪

FZU 2150 Fire Game

Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u

Description

Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of this board is consisting of grass or just empty and then they start to fire all the grass. Firstly they choose two grids which are consisting of grass and set fire. As we all know, the fire can spread among the grass. If the grid (x, y) is firing at time t, the grid which is adjacent to this grid will fire at time t+1 which refers to the grid (x+1, y), (x-1, y), (x, y+1), (x, y-1). This process ends when no new grid get fire. If then all the grid which are consisting of grass is get fired, Fat brother and Maze will stand in the middle of the grid and playing a MORE special (hentai) game. (Maybe it’s the OOXX game which decrypted in the last problem, who knows.)

You can assume that the grass in the board would never burn out and the empty grid would never get fire.

Note that the two grids they choose can be the same.

Input

The first line of the date is an integer T, which is the number of the text cases.

Then T cases follow, each case contains two integers N and M indicate the size of the board. Then goes N line, each line with M character shows the board. “#” Indicates the grass. You can assume that there is at least one grid which is consisting of grass in the board.

1 <= T <=100, 1 <= n <=10, 1 <= m <=10

Output

For each case, output the case number first, if they can play the MORE special (hentai) game (fire all the grass), output the minimal time they need to wait after they set fire, otherwise just output -1. See the sample input and output for more details.

Sample Input

4
3 3
.#.
###
.#.
3 3
.#.
#.#
.#.
3 3
...
#.#
...
3 3
###
..#
#.#

Sample Output

Case 1: 1
Case 2: -1
Case 3: 0
Case 4: 2
/*/
今天打了一场团队练习赛,打得我痛苦不堪,全场除了水题秒A了外,这个题目就坑了好久啊,最后三个人一起去强行各种枝剪,终于压时间A了这个题目,写了快5K的代码,而且过的时候还是968MS,或呵呵呵呵。。。

【= =】。

赛后,再对自己的代码优化了之后,到了500MS了,最快的一个,终于舒心了,问了下其他人的写法,神奇的纯BFS。。

虽然没有我优化后的快,但是5K代码还是有点。。。

不多说了,还是来看下这个题目。

题意是,一个人有两把火,他要烧掉整个操场里的所有草,火只能像四个方向,沿着草堆蔓延。问,是否能烧完,最少时间是多少。
/*/
/*/
这里还是先讲下我的代码。。。

代码优化改的一塌糊涂的。。。

先是DFS【顺便染色 ‘1’  ‘2’ 】一下,判断是否存在两个以下的联通块,如果超过了就标记不能烧完,直接输出,跳回循环。

如果DFS判断有2个联通块,分颜色去分别BFS两个联通块烧干净需要的最少时间,每次更新这个时间。最后输出两个颜色的最大时间。

如果DFS只有1个联通块。。。
这里我写了一个双向BFS,以前从没写过,居然一下就写对了!去找怎么才会烧的最快,返回最快需要的时间。每次向小更新这个时间。输出这个最小时间就行了。

可是各种枝剪。。。我把一些枝剪的地方标记起来了。。。。

上代码。。。。

AC代码: 【这个代码是比赛时候写的,有点改的太乱了。没事下面还有一个纯BFS的写法,学那个,那个性价比更高。】
/*/
#include"algorithm"
#include"iostream"
#include"cstring"
#include"vector"
#include"string"
#include"cstdio"
#include"queue"
#include"cmath"
using namespace std;
#define memset(x,y) memset(x,y,sizeof(x))
#define memcpy(x,y) memcpy(x,y,sizeof(x))
typedef long long LL;

const int MX = 15;

struct Vnode {
	int x,y;
} vis_node[105];

struct node {
	int x,y,t;
	node(int xx,int yy,int tt):x(xx),y(yy),t(tt) {};
	node() {};
} ;
node ans1(0,0,99),ans2(0,0,99);
char s[MX][MX];
bool vis[MX][MX];
int num;
int flag=1;
//bool ifgo[15][15][15][15];
int erear =0;
int cnt,maxx;

void BFS1(int x,int y,int n,int m,int t,int k,int l) {
	queue<node > Q;
	while(!Q.empty())Q.pop();
//	cout<<"s[x][y]=="<<s[x][y]<<endl;
	if(s[x][y]==‘1‘||s[x][y]==‘2‘) {
		node a;
		a.t=t;
		a.x=x;
		a.y=y;
//		vis[A.x][A.y]=1;
		Q.push(a);
		node b;
		b.t=t;
		b.x=k;
		b.y=l;
		Q.push(b);
	}
	while(!Q.empty()) {
		node A=Q.front();
		Q.pop();
		if(s[A.x][A.y]!=‘1‘&&s[A.x][A.y]!=‘2‘)continue ;
		if(vis[A.x][A.y])continue;
		if(s[A.x][A.y]==‘1‘)ans1=A;
		if(s[A.x][A.y]==‘2‘)ans2=A;
		vis[A.x][A.y]=1;

                //避免重复入队列。。
		if(A.x+1< n&&vis[A.x+1][A.y]==0)Q.push(node(A.x+1,A.y,A.t+1));
		if(A.x-1>=0&&vis[A.x-1][A.y]==0)Q.push(node(A.x-1,A.y,A.t+1));
		if(A.y+1< m&&vis[A.x][A.y+1]==0)Q.push(node(A.x,A.y+1,A.t+1));
		if(A.y-1>=0&&vis[A.x][A.y-1]==0)Q.push(node(A.x,A.y-1,A.t+1));
	}
}

void BFS2(int x,int y,int n,int m,int t) {
	queue<node > Q;
	while(!Q.empty())Q.pop();
//	cout<<"s[x][y]=="<<s[x][y]<<endl;
	if(s[x][y]==‘1‘||s[x][y]==‘2‘) {
		node a;
		a.t=t;
		a.x=x;
		a.y=y;
//		vis[A.x][A.y]=1;
		Q.push(a);
	}
	while(!Q.empty()) {
		node A=Q.front();
		Q.pop();
		if(s[A.x][A.y]!=‘1‘&&s[A.x][A.y]!=‘2‘)continue ;
		if(s[A.x][A.y]==‘1‘)ans1=A;
		if(s[A.x][A.y]==‘2‘)ans2=A;
		vis[A.x][A.y]=1; 

                 //避免重复入队列。。
		if(A.x+1< n&&vis[A.x+1][A.y]==0)Q.push(node(A.x+1,A.y,A.t+1));
		if(A.x-1>=0&&vis[A.x-1][A.y]==0)Q.push(node(A.x-1,A.y,A.t+1));
		if(A.y+1< m&&vis[A.x][A.y+1]==0)Q.push(node(A.x,A.y+1,A.t+1));
		if(A.y-1>=0&&vis[A.x][A.y-1]==0)Q.push(node(A.x,A.y-1,A.t+1));

	}
}

void DFS_is_2(int x,int y,int n,int m) {
	if(x<0||y<0||x>=n||y>=m)return ;
	if(vis[x][y])return ;
	if(s[x][y]==‘#‘) {
		if(flag) {
			flag=0;
			num++;
		}
		if(num==1&&s[x][y]==‘#‘)s[x][y]=‘1‘;//用‘1’  ‘2’ 染色
		if(num==2&&s[x][y]==‘#‘)s[x][y]=‘2‘;
		vis[x][y]=1;
		vis_node[erear].x=x;  //数组记录整个草地的坐标,避免BFS的时候再去从空地开始搜索。。
		vis_node[erear].y=y;
		erear++;
		if(x-1>=0&&vis[x-1][y]==0)DFS_is_2(x-1,y,n,m); //避免重复DFS。。
		if(x+1< n&&vis[x+1][y]==0)DFS_is_2(x+1,y,n,m);
		if(y-1>=0&&vis[x][y-1]==0)DFS_is_2(x,y-1,n,m);
		if(y+1< m&&vis[x][y+1]==0)DFS_is_2(x,y+1,n,m);
	}
}

int main() {
	int T,n,m;
	scanf("%d",&T);
	for(int qq=1; qq<=T; qq++) {
		scanf("%d%d",&n,&m);
		num=0;
		memset(vis,0);
		memset(s,0);
		int sign=1;
		erear=0;
		for(int i=0; i<n; i++) {
			scanf("%s",s[i]);
		}
		for(int i=0; i<n; i++) {
			for(int j=0; j<m; j++) {
				if(s[i][j]==‘.‘)continue; //如果这个点是空地就不去DFS搜索。。
				flag=1;
				DFS_is_2(i,j,n,m);
			}
		}
		if(num>2)sign=0;

//		puts("");
//		for(int i=0;i<n;i++)puts(s[i]);//画图确认染色
//		puts("");

		memset(vis,0);
		maxx=0x3f3f3f3f;
		node ans22(0,0,99),ans11(0,0,99);
		ans1.t=99,ans2.t=99;
		if(sign) {
			for(int i=0; i<erear; i++) {
				if(num==1) {
					for(int j=i; j<erear; j++) { //这个地方最后一次优化的,避免重复搜索,直接砍掉一半的复杂度,从968ms 砍到了500ms;
//						cout<<"insert OK"<<endl;
						memset(vis,0);
						BFS1(vis_node[i].x,vis_node[i].y,n,m,0,vis_node[j].x,vis_node[j].y);
						if(ans22.t>ans2.t)ans22=ans2;if(ans11.t>ans1.t)ans11=ans1;
					}
				} else {
					if(s[vis_node[i].x][vis_node[i].y]==‘1‘||s[vis_node[i].x][vis_node[i].y]==‘2‘) {
						memset(vis,0);
						BFS2(vis_node[i].x,vis_node[i].y,n,m,0);
						if(ans22.t>ans2.t)ans22=ans2;if(ans11.t>ans1.t)ans11=ans1;
					}
				}
			}
		}
		if(sign) {
			if(num==0)
				printf("Case %d: %d\n",qq,0);
			else if(num==1)
				printf("Case %d: %d\n",qq,(ans11.t));
			else
				printf("Case %d: %d\n",qq,max(ans11.t,ans22.t));
		} else printf("Case %d: %d\n",qq,-1);
	}
	return 0;
}

/*   下面是超级数据

15
5 5
..#..
..#..
#####
..#..
..#..

5 5
#####
..#..
#####
..#..
#####
5 5
#####
#...#
#...#
#...#
#####
1 1
.
3 3
.#.
###
.#.
3 3
.#.
#.#
.#.
3 3
...
#..
...
3 3
###
..#
#.#

9 9
#########
#.......#
#########
#.......#
#########
#.......#
#########
#.......#
#########
10 10
##########
##########
##########
##########
##########
##########
##########
##########
##########
##########

5 3
#.#
.##
###
.##
.##

*/


/*/
这是从学长那里拜师学来的写法。

也是双向BFS。

直接两个点开始搜索,用tot记录每次搜索烧过的点。如果tot=sz(草堆的数量)那么输出这时候记录的t的值就行了,然后维护这个t的值求出最小。

如果不能烧完,那么返回一个无穷大,最后再判断一下,是否有答案,输出答案或者-1.

这个写法太神奇了,同样要用到我上面这段代码的一些枝剪。

耗时560ms,比我上面的慢一点点,但是代码量也少了好多。。。

AC代码:
/*/
#include"algorithm"
#include"iostream"
#include"cstring"
#include"vector"
#include"string"
#include"cstdio"
#include"queue"
#include"cmath"
using namespace std;
#define memset(x,y) memset(x,y,sizeof(x))
#define memcpy(x,y) memcpy(x,y,sizeof(x))
typedef long long LL;

int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}};

int sz,T,n,m;

char s[12][12];

bool vis[12][12];

struct node {
	int x,y,t;
	node() {};
	node(int xx,int yy,int tt):x(xx),y(yy),t(tt) {};
};

struct Togo {
	int x,y;
} togo[200];

int BFS(int u,int v,int k) {
	int tot=0;
	queue<node > Q;
	while(!Q.empty())Q.pop();
	node a;
	a.x=togo[u].x,a.y=togo[u].y,a.t=k;
	vis[a.x][a.y]=1;//标记起点
	Q.push(a);
	node b;
	b.x=togo[v].x,b.y=togo[v].y,b.t=k;
	vis[b.x][b.y]=1;//标记起点
	Q.push(b);
	while(!Q.empty()) {
		node A=Q.front();
		Q.pop();
		tot++;
		if(tot==sz) return A.t;
		for(int i=0; i<4; i++) {
			int nx=A.x+dir[i][0],ny=A.y+dir[i][1];
			if(nx<0||ny<0||ny>=m||nx>=n||s[nx][ny]==‘.‘||vis[nx][ny])continue; //判断越界,或者是否已经搜索过了。
			Q.push(node(nx,ny,A.t+1));
			vis[nx][ny]=1;//标记已经搜索过,避免重复入队列。
		}
	}
	return 1e9+5;
}

int main() {
	scanf("%d",&T);
	for(int qq=1; qq<=T; qq++) {
		sz=0;
		scanf("%d%d",&n,&m);
		for(int i=0; i<n; i++) {
			scanf("%s",s[i]);
			for(int j=0; j<m; j++) {
				if(s[i][j]==‘#‘) {
					togo[sz].x=i; //将草堆坐标压入数组
					togo[sz].y=j;
					sz++;
				}
			}
		}
		int ans = 1e9+5;
		int c=ans;
		for(int i=0; i<sz; i++) {
			for(int j=i; j<sz; j++) { //避免重搜,和我最后一次优化一样。
				memset(vis,0);
				ans=min(ans,BFS(i,j,0));
			}
		}
		printf("Case %d: %d\n",qq,ans==c ?-1:ans);
	}
	return 0;
}

  

 
时间: 2024-08-05 07:06:52

ACM: FZU 2150 Fire Game - DFS+BFS+枝剪 或者 纯BFS+枝剪的相关文章

FZU 2150 Fire Game(DFS+BFS)

题意  在n*m个格子组成的草地上   你可以选择两个是草('#')的格子点燃  每个点燃的格子在下一秒其四个相邻的是草的格子也会被点燃   问点燃所有的草至少需要多少秒 DFS和BFS的综合  如果'#'连通块的数量大于2个是肯定不能点燃所有的  先dfs判断连通块个数  再bfs找出选哪两个格子可以最快把草烧完 #include <map> #include <cstdio> #include <cstring> using namespace std; const

FZU 2150 Fire Game(点火游戏)

p.MsoNormal { margin: 0pt; margin-bottom: .0001pt; text-align: justify; font-family: Calibri; font-size: 10.5000pt } h2 { margin-top: 5.0000pt; margin-bottom: 5.0000pt; text-align: left; font-family: 宋体; font-weight: bold; font-size: 18.0000pt } h3 {

FZU 2150 Fire Game --两点同步搜索

枚举两点,然后同步BFS,看代码吧,很容易懂的. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> #include <queue> #define Mod 1000000007 using namespace std; struct Po

(FZU 2150) Fire Game (bfs)

题目链接:http://acm.fzu.edu.cn/problem.php?pid=2150 Problem Description Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of this board is consisting of grass or just empty a

FZU 2150 Fire Game (暴力BFS)

[题目链接]click here~~ [题目大意]: 两个熊孩子要把一个正方形上的草都给烧掉,他俩同时放火烧,烧第一块的时候是不花时间的,每一块着火的都可以在下一秒烧向上下左右四块#代表草地,.代表着不能烧的.问你最少花多少时间可以烧掉,如果烧不掉就输出-1 [解题思路]: 数据比较弱的情况下直接暴力枚举每块草坪上可以放的位置,比较高端的写法目前没有想到,以后想到了文章更新下~~ ps:由于一个细节没注意,导致WA了几乎一页,还以为FZU 判题出错了,后来突然发现每次从队列里拿出队首的元素,才是

fzu 2150 Fire Game 【技巧BFS】

题目:fzu2150 Fire Game 题意:给出一个m*n的图,'#'表示草坪,' . '表示空地,然后可以选择在任意的两个草坪格子点火,火每 1 s会向周围四个格子扩散,问选择那两个点使得燃烧所有的草坪花费时间最小? 分析:这个题目如果考虑技巧的话有点难度,但是鉴于数据范围比较小,我们可以暴力枚举任意的草坪所在的点,然后两个点压进队列里面BFS,去一个满足条件的最小值即可. 顺便说一下 fzu 2141 Sub-Bipartite Graph 的思路,比赛的时候没有做出来. 这个题目想的复

FZU 2150 Fire Game(BFS)

Problem Description Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of this board is consisting of grass or just empty and then they start to fire all the grass. Firstl

FZU 2150 Fire Game(BFS)

Fire Game Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description Fat brother and Maze are playing a kind of special (hentai) game on an N*M board (N rows, M columns). At the beginning, each grid of this board is cons

FZU - 2150 Fire Game(两点bfs)

题目大意: 给你一个n*m的图,里面有草也有空地(#代表草).现在有两个人各在一块草地点火要烧掉这些草,并且燃烧的草可以向上下左右四个方向蔓延,问最少多长时间可以将所有的草都烧完,不能全部烧完输出-1. 两个起点的BFS,感觉和求最短路差不多,依次枚举两个起点,找到步数最多的那个草地,再从每次枚举的结果中找最小的. #include<iostream> #include<cstdio> #include<cmath> #include<algorithm>