#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int INF = 1e6;
const int maxn = 10 + 5;
char board[maxn][maxn]; //记录草坪
int sign[maxn][maxn]; //访问后标记
int dx[4] = {-1, 1, 0, 0}; //x的移动
int dy[4] = {0, 0, -1, 1}; //y的移动
int t, n, m, TIME, cas, ans;
bool flag;
struct Step
{
int x;
int y;
int step;
} Maze, Fat; //记录选择的第一个坐标 和 选择的第二个坐标 step记录步数
queue<Step> fire;
bool judge(int x, int y) //判断是否超出范围 是否是草 是否没烧过
{
if(x >= 0 && y >= 0 && x < n && y < m && board[x][y] == '#' && !sign[x][y])
return true;
else
return false;
}
void bfs()
{
memset(sign, 0, sizeof(sign)); //每次重新选择 点 的时候,都要重置访问数组
sign[Maze.x][Maze.y] = 1; //标记第一个点
sign[Fat.x][Fat.y] = 1; //标记第二个点
while(!fire.empty()) //清空队列
fire.pop();
fire.push(Maze); //放入第一个点
fire.push(Fat); //放入第二个点
while(!fire.empty())
{
Step Now = fire.front();
ans = Now.step; //ans记录步数(即 题目中的时间)
fire.pop();
for(int i = 0 ; i < 4; i ++) //上下左右四个方向烧
{
Step Next = Now;
Next.x += dx[i];
Next.y += dy[i];
Next.step ++; //移动一次 步数 +1
if(judge(Next.x, Next.y))
{
fire.push(Next); //满足条件 投入队列
sign[Next.x][Next.y] = 1; //标记
}
}
}
return;
}
int main()
{
scanf("%d", &t);
cas = 0;
while(t --)
{
scanf("%d %d", & n, & m);
for(int i = 0; i < n; i ++)
scanf("%s", board[i]); //输入草坪
TIME = INF;
int cnt = 0;
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++)
if(board[i][j] == '#')
cnt ++; //记录草的数量 只为草的数量 = 1 时做准备
if(cnt == 1) //如果草的数量 = 1,则直接输出0
{
printf("Case %d: 0\n", ++ cas);
continue;
}
for(int i1 = 0; i1 < n; i1 ++)
{
for(int j1 = 0; j1 < m; j1 ++)
{
if(board[i1][j1] == '#') //找到第一个草的位置
{
Maze.x = i1;
Maze.y = j1;
Maze.step = 0;
for(int i2 = i1; i2 < n; i2 ++) //向下枚举第二个草的位置
{
for(int j2 = 0; j2 < m; j2 ++)
{
if(i2 == i1 && j2 <= j1) //避免重复
continue;
if(board[i2][j2] == '#') //找到第二个草的位置
{
Fat.x = i2;
Fat.y = j2;
Fat.step = 0;
bfs(); //bfs
bool flag = true; //初始flag
for(int i = 0; i < n; i ++)
{
for(int j = 0; j < m; j ++)
{
if(board[i][j] == '#' && !sign[i][j]) //如果平地上还有未烧掉的草 则此方案是不行的
{
flag = false; //不行
break; //并跳出
}
}
if(!flag)
break;
}
if(flag) //如果flag还是true 则说明草烧光 满足题意
TIME = min(TIME, ans); //取最小步数(最少的时间)
}
}
}
}
}
}
if(TIME != INF) //当 TIME != INF 说明有解
printf("Case %d: %d\n", ++ cas, TIME);
else
printf("Case %d: -1\n", ++ cas);
}
return 0;
}
题意:
两个熊孩子 Fat、Maze 要放火烧草(#)。火成十字形蔓延(上下左右)。空地 . 不能烧到。他们各点燃一个地方,问最少多少时间烧光草(点燃的地方时间 = 0),输出最短时间,不能烧完则输出-1。
题解:
下午两个小时。晚上三个小时。一开始的思路是对的,就是枚举(题目数据小)所有两个人可以点的地方并且用BFS标记计步数,最后取最小值。
错误1:比赛的时候我认为投入两个元素,就应该2个同时进行入队出队以达到时间同步的目的,后来才知道,其实一直只要一个Now和一个Next就行,不用Now1、Now2、Next1、Next2这样同时进行(反而错)。因为Now记录每一步的信息,时间同步体现在step和出队顺序,sign标记顺序依旧是正确的(从两点开始 不断蔓延,并不是一点蔓延完全,才蔓延另一点)。ans不需要取最大值(ans = max(ans, Next.step)),最后一步一定是这两点烧草的时间。
错误2:手贱 + 脑残 吧,打错94行(for(int j2 = j2 + 1; j2 < m; j2 ++)),
导致一直不能进入此循环…..不过即使将j2 + 1改为j1 + 1也是错误的,(居然一开始没意识到..)因为循环到下一行后每次列都是从 j1 + 1开始,导致枚举出现遗漏。
错误3:
将所有的小错误消除后,例子也是对的,满怀信心地把代码扔进去检验,结果WA了…真检查不出来,觉得已经完美了,后来发现自己进行bfs的前提是要有两个草,如果总共只有一个草是不可能进入到下面的循环并判断的,所以将草的数量为1的时候 拿出来另外处理(也就是为什么要cnt计数了),