FZU - 2150 Fire Game (kuangbin - 简单搜索)

题目描述(已转换成中文)

  两个熊孩子在n*m的平地上放火玩,#表示草,两个熊孩子分别选一个#格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一。求烧完所有的草需要的最少时间。如不能烧完输出-1。

输入格式

  第一行,输入一个T,表示有T组测试数据。 每组数据由一个n,m分别表示行列
1 <= T <=100, 1 <= n <=10, 1 <= m <=10

输出格式

  输出最少需要的时间

输入输出样例
输入

4
3 3
.#.

.#.
3 3
.#.
#.#
.#.
3 3

#.#

3 3

…#
#.#

输出

Case 1: 1
Case 2: -1
Case 3: 0
Case 4: 2

题目链接

分析:

  因为这道题数据量不大,最简单的做法就是双起点bfs,选择两个草堆同时开始燃烧,暴力枚举两个草堆并同时加入队列。首先,在每组测试数据读入前,把u数组清零,u是一个四维数组,u[i][j][ii][jj]是表示选择坐标(i, j)和(ii, jj)的草作为燃烧的起点。读入n * m矩阵的具体字符的时候,统计有多少草堆,如果草堆的数量少于3,则两个小屁孩一步就能把草烧完,所以直接输出0;否则用四层循环枚举两个坐标,如果两个坐标所在的位置都是草堆且vis[i][j][ii][jj]为0,则把vis[i][j][ii][jj]标记为1,且要把vis[ii][jj][i][j]标记为1(避免重复),并把这两个点看成是起点,通过bfs函数计算出烧完草堆所用的最短时间。这样对于每两个是草堆的坐标都计算出他们作为bfs的起点时烧完草堆所用的最短时间,最后取每种情况的最小值。在bfs函数中还要记录烧过的草堆总数,执行完bfs函数后,要判断原矩阵中的草堆是否已全被烧完,若还有草堆没被烧,则这种情况得到的最短时间无效。若到最后所有坐标都遍历过了,ans的值还是N没更新,说明矩阵中的草堆不只只有两个连通块,所以两个小屁孩每人烧一个连通块的话,草堆也烧不完,所以输出-1。

这道题需要注意的地方:

  这道题我走了很多弯路,先说说我的错误做法,首先,我先统计n * m的平地上有多少连通块,并且把第i个连通块里的字符都改成’i’,因为只有两个小屁孩在放火,并且每人只放一次,所以当连通块的数量比2大的时候就输出-1,接着对两个连通块分别进行常规的bfs操作,分别求出两个连通块中最早烧完整个连通块的最短时间,再取两个最短时间中的最大值输出即可,但是很遗憾wa了…因为我忽略了一个细节,我只考虑了连通块的数量大于2时就输出-1,否则就直接对两个连通块求最短时间,其实还没考虑当连通块为1时的情况,所以不能用这种方法做,虽然看似比暴力选择两个草堆同时开始燃烧要减少很多时间。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
int n, m, s1, sum, z;
char a[15][15];
int x3, y3, x2, y2, u[15][15][15][15], vis[15][15];
int b[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
const int N = 105;
int read(){
	int x, f = 1;
	char ch;
	while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
	x = ch - '0';
	while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
	return x * f;
}
struct node{
	int x, y, ans;
};
void bfs(){
	memset(vis, 0, sizeof(vis));
	queue <node> q;
	node t, r;
	sum = 0;
	vis[x3][y3] = 1;
	vis[x2][y2] = 1;
	t.x = x3;
	t.y = y3;
	t.ans = 0;
	r.x = x2;
	r.y = y2;
	r.ans = 0;
	q.push(t);
	q.push(r);
	while(!q.empty()){
		t = q.front();
		q.pop();
		for(int i = 0; i < 4; i++){
			r.x = t.x + b[i][0];
			r.y = t.y + b[i][1];
			if(a[r.x][r.y] == '#' && 0 <= r.x && r.x < n && r.y >= 0 && r.y < m && !vis[r.x][r.y]){
				vis[r.x][r.y] = 1;
				r.ans = t.ans + 1;
				z++;
				q.push(r);
			}
		}
		sum = max(sum, t.ans);
	}
}
int main(){
	int ans, i, j, ii, jj, k, t = read();
	for(k = 1; k <= t; k++){
		ans = N;
		s1 = 0;
		memset(u, 0, sizeof(u));
		n = read();
		m = read();
		for(i = 0; i < n; i++){
			for(j = 0; j < m; j++){
				scanf("%c", &a[i][j]);
				if(a[i][j] == '#') s1++;
			}
			getchar();
		}
		if(s1 < 3){
			printf("Case %d: 0\n", k);
			continue;
		}
		for(i = 0; i < n; i++){
			for(j = 0; j < m; j++){
				if(a[i][j] != '#') continue;
				for(ii = 0; ii < n; ii++){
					for(jj = 0; jj < m; jj++){
						if(a[i][j] == '#' && a[ii][jj] == '#' && (i != ii || j != jj)){
							if(u[i][j][ii][jj]) continue;
							u[i][j][ii][jj] = u[ii][jj][i][j] = 1;
							x3 = i;
							y3 = j;
							x2 = ii; 
							y2 = jj;
							z = 2;
							bfs();
							if(s1 == z && sum < ans) ans = sum; 
						}
					}
				}
			}
		}
		if(ans == N) printf("Case %d: -1\n", k);
		else printf("Case %d: %d\n", k, ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值