1. 程式人生 > 實用技巧 >UVA 11624 Fire!

UVA 11624 Fire!

題目如下:

喬在迷宮中工作。不幸的是,迷宮的一部分著火了,迷宮的主人沒有制定火災的逃跑計劃。請幫助喬逃離迷宮。根據喬在迷宮中的位置以及迷宮的哪個方塊著火,你必須確定火焰燒到他之前,喬是否可以離開迷宮,如果能離開他能跑多快。

喬和火每分鐘移動一個方格,上、下、左、右,四個方向中的一個。火勢向四個方向同時蔓延。喬可以從迷宮的任何一個邊界逃離迷宮。無論是喬還是火都不會到達有牆的位置。


輸入

第一行輸入包含一個整數,即測試次數

每個測試用例的第一行包含兩個

整數R和C,用空格分隔,1≤R,C≤1000

下面R行中,每一行都包含C個字元,以及每個字元是以下之一:

# 代表牆

. 代表空地,火和喬是可通行的

J 喬在迷宮中最初的位置,火和喬是可通行的

F 代表火

在每組測試中只有一個J


輸出

對於每個測試用例,如果在火蔓延的時候燒到了喬,則喬無法逃出迷宮,輸出'IMPOSSIBLE'如果喬能逃出迷宮,則輸出喬最快可以在幾分鐘內安全逃出迷宮,每組輸出佔一行


樣例輸入

2

4 4

#JF#

#..#

#..#

3 3

#J.

#.F


樣例輸出

3

IMPOSSIBLE


解題思路:

首先,這道題跟之前所做所有bfs問題的遍歷方式不一樣,之前所做的問題都是遍歷四個方向後(這裡以四個方向舉例)從四個方向(A B C D)中選取一個方向A,之後按照這個方向A再次遍歷4個方向,遍歷完之後,捨棄方向A,去遍歷方向B,之後以此類推。但是,這道題是從起始點開始遍歷四個方向(A B C D) 後,將4個方向(A,B,C,D)同時進行遍歷(這時就會有16個方向)。然後以此類推。因此,我們就不能按照原先的模板來書寫bfs了。因為我們要將所有在佇列中的點都要去遍歷一遍。因此為了達到這樣的效果,我們可以事先去計算佇列中的長度,之後根據佇列中的長度來寫一個for迴圈,這樣的話就會保證佇列中所有的點都會被遍歷一遍。(在這道題中人和火都是這樣的遍歷方式)


其次,在這道題中火不只有一個,可能會有多個的存在。並且,在這道題中由於人逃出邊界就算勝利,因此我們要考慮下標的情況。不能讓下標出現負數,否則的話就會RE!因此,我們選擇下標為(1-R,1-C)而不是(0-R-1,0-R-1)!


判斷逃出邊界的方式共有兩種,第一種是當人的x座標等於1或者R時,y座標等於1或者C時,就算逃出去了。為什麼可以這樣判斷? 因為,當人的座標在邊界時(因為1和R和C都是屬於圖的邊界座標)他下一步肯定會逃出去,不可能出現被燒死的情況。所以,這道題中判斷逃出邊界方式實際上就為:當人到達邊界時就算成功!但是這樣判斷的話,由於人在逃出去的時候,座標肯定在邊界外面,所以當我們判斷成功後,要將步數+1。


第二種是當人的x座標等於0或者R+1時,y座標等於0或者C+1時,就算逃出去了。這樣做跟第一種的區別就是當人的座標在邊界外時,才判斷他逃了出去。(第一種方式則是:當人的座標在邊界時,就判斷他逃了出去)似乎第二種更容易進行理解。但是請注意:使用第二種方式時,由於這道題是多組輸入輸出,每組測試例項中的地圖都不一樣。因此,我們需要在每組測試之前都要去清一下地圖才可。(想一下為什麼要這樣)


其它的內容都跟正常的bfs一樣,並且推薦在使用bfs進行搜尋時,採用結構體來儲存搜尋到的座標。而儘量不要使用pair,用pair不好處理!


程式碼如下:(第一種方式)

#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
char Graph[1500][1500];        //定義圖
int R, C;                      //定義R行,C列
int Count;                     //代表測試次數
bool vis[1500][1500];          //代表訪問陣列(人)
int x[4] = { 1,0,-1,0 };       //定義方向陣列x
int y[4] = { 0,1,0,-1 };       //定義方向陣列y
struct Point                   //定義代表頂點的結構體
{
	int x;
	int y;
	int step;
};
deque<struct Point> fire;                //代表火進行bfs的佇列
deque<struct Point> Person;              //代表人進行bfs的佇列
int bfs(struct Point start);             //代表進行bfs的函式
void init();                             //代表進行初始化的函式
void firemove();                         //代表火進行蔓延的函式

void init()
{
	int i, j;
	for (i = 0; i < 1500; i++)
	{
		for (j = 0; j < 1500; j++)
		{
			vis[i][j] = false;
		}
	}
	fire.clear();                    //清佇列(人)
	Person.clear();                  //清佇列(火)
}

void firemove()
{
	int i, j;
	struct Point firetemp;         //代表當前遍歷的火的下標
	int movex, movey;              //代表火移動之後的下標
	int firecount = fire.size();   //代表火的個數
	for (i = 0; i < firecount; i++)
	{
		firetemp = fire.front();
		fire.pop_front();
		for (j = 0; j < 4; j++)
		{
			movex = firetemp.x + x[j];                  //代表火嘗試向四個方向進行蔓延
			movey = firetemp.y + y[j];
			if (movex >= 1 && movex <= R && movey >= 1 && movey <= C && Graph[movex][movey] == '.')   //火只能在迷宮中進行燃燒,因此火的座標不可出邊界
			{
				fire.push_back({ movex,movey,0 });          //將火進行入隊
				Graph[movex][movey] = 'F';                  //將燃燒的地方進行修改為F,代表燃燒過的地方就不能走了
			}
		}
	}

}

int bfs(struct Point start)
{
	int movex, movey;                  //代表人移動之後的座標
	struct Point temp;
	int personcount;                   //定義人的座標個數
	int i;
	Person.push_back({ start.x,start.y,start.step }); //代表從起點開始入隊
	vis[start.x][start.y] = true;                     //代表起點已經訪問過。
	while (!Person.empty())
	{
		personcount = Person.size();
		firemove();                             //代表讓火先走一步
		while (personcount--)                   //代表人也可以一次性往四個方向去行走
		{
			temp = Person.front();
			Person.pop_front();
			if (temp.x == 1 || temp.x == R || temp.y == 1 || temp.y == C)  //如果人的座標出了邊界
			{
				return temp.step + 1;                                        //返回出界座標所對應的步數
			}
			for (i = 0; i < 4; i++)
			{
				movex = temp.x + x[i];                       //代表人嘗試向四個方向行走
				movey = temp.y + y[i];
				if (movex >= 1 && movex <= R && movey >= 1 && movey <= C && vis[movex][movey] == false && Graph[movex][movey] == '.')     //依據本題題意,人的座標是可以出邊界的,如果出了邊界代表逃脫成功
				{
					vis[movex][movey] = true;                                   //代表目前點已經走過
					Person.push_back({ movex,movey,temp.step + 1 });               //將移動後的座標進行入隊,並且步數加1
				}

			}
		}
	}
	return -100;                 //如果迴圈結束,函式仍沒有結束的話,代表人不可能逃脫
}

int main()
{
	int i, j, k;
	int result;           //代表儲存結果的變數
	while (scanf("%d", &Count) != EOF)
	{
		for (k = 0; k < Count; k++)
		{
			init();                                 //執行初始化函式
			scanf("%d %d", &R, &C);
			for (i = 1; i <= R; i++)
			{
				for (j = 1; j <= C; j++)
				{
					scanf(" %c", &Graph[i][j]);
					if (Graph[i][j] == 'F')         //如果這個座標為火(在這道題中不止一個火)
					{
						fire.push_back({ i,j,0 });  //將火入佇列
					}
				}
			}
			for (i = 1; i <= R; i++)
			{
				for (j = 1; j <= C; j++)
				{
					if (Graph[i][j] == 'J')
					{
						result = bfs({ i,j,0 });         //代表以喬為起點進行bfs
						if (result == -100)
						{
							cout << "IMPOSSIBLE" << endl;
						}
						else
						{
							cout << result << endl;
						}
					}
				}
			}
		}

	}
}

程式碼如下:(第二種方式)

#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
char Graph[1500][1500];        //定義圖
int R, C;                      //定義R行,C列
int Count;                     //代表測試次數
bool vis[1500][1500];          //代表訪問陣列(人)
int x[4] = { 1,0,-1,0 };       //定義方向陣列x
int y[4] = { 0,1,0,-1 };       //定義方向陣列y
struct Point                   //定義代表頂點的結構體
{
	int x;
	int y;
	int step; 
};
deque<struct Point> fire;                //代表火進行bfs的佇列
deque<struct Point> Person;              //代表人進行bfs的佇列
int bfs(struct Point start);             //代表進行bfs的函式
void init();                             //代表進行初始化的函式
void firemove();                         //代表火進行蔓延的函式

void init()
{
	int i, j;
	for (i = 0; i < 1500; i++)
	{
		for (j = 0; j < 1500; j++)
		{
			Graph[i][j] == '.';
			vis[i][j] = false;
		}
	}
	fire.clear();                    //清佇列(人)
	Person.clear();                  //清佇列(火)
}

void firemove()
{
	int i,j;
	struct Point firetemp;         //代表當前遍歷的火的下標
	int movex, movey;              //代表火移動之後的下標
	int firecount = fire.size();   //代表火的個數
	for (i = 0; i < firecount; i++)
	{
		firetemp = fire.front();
		fire.pop_front();
		for (j = 0; j < 4; j++)
		{
			movex = firetemp.x + x[j];                  //代表火嘗試向四個方向進行蔓延
			movey = firetemp.y + y[j];
			if (movex >= 1 && movex <= R && movey >= 1 && movey <= C && Graph[movex][movey] == '.')   //火只能在迷宮中進行燃燒,因此火的座標不可出邊界
			{
				fire.push_back({ movex,movey,0 });          //將火進行入隊
				Graph[movex][movey] = 'F';                  //將燃燒的地方進行修改為F,代表燃燒過的地方就不能走了
			}
		}
	}

}

int bfs(struct Point start)
{
	int movex, movey;                  //代表人移動之後的座標
	struct Point temp;
	int personcount;                   //定義人的座標個數
	int i;
	Person.push_back({ start.x,start.y,start.step }); //代表從起點開始入隊
	vis[start.x][start.y] = true;                     //代表起點已經訪問過。
	while (!Person.empty())
	{
		personcount = Person.size();
		firemove();                             //代表讓火先走一步
		while (personcount--)                   //代表人也可以一次性往四個方向去行走
		{
			temp = Person.front();
			Person.pop_front();
			if (temp.x == 0 || temp.x == R+1 || temp.y == 0 || temp.y == C+1)  //如果人的座標出了邊界
			{
				return temp.step;                                        //返回出界座標所對應的步數
			}
			for (i = 0; i < 4; i++)
			{
				movex = temp.x + x[i];                       //代表人嘗試向四個方向行走
				movey = temp.y + y[i];
				if (vis[movex][movey] == false && Graph[movex][movey] == '.' || (movex == 0 || movex == R+1 || movey == 0 || movey == C+1) )     //依據本題題意,人的座標是可以出邊界的,如果出了邊界代表逃脫成功
				{
					vis[movex][movey] = true;                                   //代表目前點已經走過
					Person.push_back({ movex,movey,temp.step+1});               //將移動後的座標進行入隊,並且步數加1
				}

			}
		}
	}
	return -100;                 //如果迴圈結束,函式仍沒有結束的話,代表人不可能逃脫
}

int main()
{
	int i,j,k;
	int result;           //代表儲存結果的變數
	while (scanf("%d",&Count) != EOF)
	{
		for (k = 0; k < Count; k++)
		{
			init();                                 //執行初始化函式
			scanf("%d %d", &R, &C);
			for (i = 1; i <= R; i++)
			{
				for (j = 1; j <= C; j++)
				{
					scanf(" %c", &Graph[i][j]);
					if (Graph[i][j] == 'F')         //如果這個座標為火(在這道題中不止一個火)
					{
						fire.push_back({ i,j,0 });  //將火入佇列
					}
				}
			}
			for (i = 1; i <= R; i++)
			{
				for (j = 1; j <= C; j++)
				{
					if (Graph[i][j] == 'J')
					{
						result = bfs({ i,j,0 });         //代表以喬為起點進行bfs
						if (result == -100)
						{
							cout << "IMPOSSIBLE" << endl;
						}
						else
						{
							cout << result << endl;
						}
					}
				}
			}
		}

	}
}