#include <iostream>
#include <cstdio>
#include <cstring>
#include <deque>
#include <algorithm>
using namespace std;
int n, m, t;
struct Point
{
int x; //代表每个点的x坐标
int y; //代表每个点的y坐标
int step; //代表到达该点的步数
Point() {} //空构造函数
Point(int x, int y,int step) : x(x), y(y),step(step) {} //在这个测试平台中,我们需要加一个构造函数,不然的话就会CE
};
deque<Point> Q; //定义进行bfs的队列
const int INF = 0x3f3f3f3f; //定义极大值
char Graph[15][15]; //定义图
int bfs(Point start); //代表进行bfs的函数
void init(); //代表进行初始化的函数
void find(Point start); //代表找寻连通块的函数
bool vis[15][15]; //代表点的访问状态
int connect[15][15]; //代表点属于哪个连通块
int Count = 0; //代表联通块数
int x[4] = { 1,0,-1,0 };//代表移动的方向(x坐标)
int y[4] = { 0,1,0,-1 };//代表移动的方向(y坐标)
int sx, sy; //代表第一个起点
int Time = INF; //定义最小的时间数
void init()
{
int i, j;
for (i = 0; i < 15; i++)
{
for (j = 0; j < 15; j++)
{
connect[i][j] = 0; //初始化为0(代表图中的草不属于任何连通块)
}
}
}
void find(Point start) //在搜索连通块的函数中,每个顶点的step没有任何作用!step是在bfs函数中进行记录最短时间用的
{
int movex, movey; //代表移动之后的坐标
int i;
Point temp; //代表当前遍历的顶点
Count++; //代表生成的是第Count连通块
connect[start.x][start.y] = Count; //代表起点属于第Count个连通块
Q.push_back(start); //将起点进行入队
while (!Q.empty())
{
temp = Q.front();
Q.pop_front();
for (i = 0; i < 4; i++)
{
movex = temp.x + x[i];
movey = temp.y + y[i];
if (movex >= 0 && movex < n && movey >= 0 && movey < m && Graph[movex][movey] == ‘#‘ && connect[movex][movey] == 0) //代表从当前点找寻下一个不属于任何连通块的点
{
Q.push_back(Point(movex,movey,0));//将搜索到的点入队列(由于之前定了一个有参构造函数,因此我们需要将参数传递进构造函数来生成一个结构体,然后再入队列)
connect[movex][movey] = Count; //将搜索到的点归属于第Count个连通块中
}
}
}
}
int bfs(Point start)
{
int i;
int movex, movey; //代表移动之后的坐标
int Max = -INF; //代表整道题所需要的最短时间(因为是以两个起点进行bfs,那么整道题中的最短时间应该取以两个起点进行燃烧的最大时间)
Point temp; //代表当前正在遍历的顶点
memset(vis, false, sizeof(vis)); //代表初始化vis数组为false
Q.push_back(Point(sx,sy,0)); //将第一个起点进行入队
Q.push_back(Point(start.x,start.y,0)); //将第二个起点进行入队
vis[sx][sy] = true; //第一个/第二个起点均被访问过
vis[start.x][start.y] = true;
while (!Q.empty())
{
temp = Q.front();
Q.pop_front();
Max = max(Max, temp.step); //取每个遍历的点的最大时间(取以两个起点进行遍历的最大时间(假设有两个联通块,每个连通块各有一个起点,假设第一个起点燃烧了1秒就结束了,但是第二个起点燃烧了3秒才结束,所以整道题的最小时间应该取3秒)
for (i = 0; i < 4; i++)
{
movex = temp.x + x[i];
movey = temp.y + y[i];
if (movex >= 0 && movex < n && movey >= 0 && movey < m && Graph[movex][movey] == ‘#‘ && vis[movex][movey] == false)
{
vis[movex][movey] = true;
Q.push_back(Point(movex,movey,temp.step + 1));
}
}
}
return Max;
}
int main()
{
int i, j; //迭代变量i,j
int k;
int a, b; //迭代变量a,b
int sum = 1; //代表示例个数
scanf("%d", &t);
for (k = 0; k < t; k++)
{
init();
scanf("%d %d", &n, &m);
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
scanf(" %c", &Graph[i][j]);
}
}
for (i = 0; i < n; i++)
{
for (j = 0; j < m; j++)
{
if (connect[i][j] == 0 && Graph[i][j] == ‘#‘) //代表将不属于任何连通块的草为起点进行遍历
{
find(Point(i, j, 0));
}
}
}
if (Count > 2) //若连通块的数量超过两个,则此题无解。(因为撑死就有两个起点进行bfs遍历,如果出现了2个以上的连通块,则肯定有连通块无法被烧)
{
cout << "Case " << sum++ << ": " << -1 << endl;
}
else
{
for (i = 0; i < n; i++) //通过循环在连通块中遍历所有的起点情况,并从这些起点遍历所花费的时间中,筛选出最小时间。
{
for (j = 0; j < m; j++)
{
if (connect[i][j] == 1) //代表在第一个连通块中寻找起点(并且在连通块中可能会以不同的起点进行遍历,从这些起点中找到最少的燃烧时间)
{
sx = i;
sy = j;
for (a = 0; a < n; a++)
{
for (b = 0; b < m; b++)
{
if (connect[a][b] == Count) //代表在第Count个连通块中寻找起点。(因为Count的个数最多等于2,当图中有两个联通块时,由于上面已经选择了第一个连通块,所以此时Count就一定为2。这样就构成了剪枝效果。如果图中只有一个联通块的话,Count就为1(代表两个起点同时在一个连通块中进行遍历)
{
Time = min(Time, bfs(Point(a, b, 0))); //代表进行bfs搜索并取以不同起点的最短时间(因为在这道题中可以任取‘#‘进行搜索,因此会有不同的起点,由于选择不同的起点,其结果也会不一样,所以我们应该选择花费时间最小的起点)
}
}
}
}
}
}
cout << "Case " << sum++ << ": " << Time << endl;
}
Time = INF;
Count = 0;
}
}
原文:https://www.cnblogs.com/gao79135/p/14113501.html