题目描述(已转换成中文)
两个熊孩子在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;
}