2018.3.19
此題我用了廣度優先搜索(BFS)的方法.廣度優先搜索不僅僅可以使用在圖的遍歷中,而且可以用於求解複雜問題的最優解。
- 引子
輸入:
輸入數據的第一行是一個正整數K,表明測試數據的數量.每組測試數據的第一行是四個正整數A,B,C和T(1<=A,B,C<=50,1<=T<=1000),它們分別代表城堡的大小和魔王回來的時間.然後是A塊輸入數據(先是第0塊,然後是第1塊,第2塊……),每塊輸入數據有B行,每行有C個正整數,代表迷宮的佈局,其中0代表路,1代表牆。
輸出:
對於每組測試數據,如果Ignatius能夠在魔王回來前離開城堡,那麼請輸出他最少需要多少分鐘,否則輸出-1.
樣例輸入:
1
3 3 4 20
0 1 1 1
0 0 1 1
0 1 1 1
1 1 1 1
1 0 0 1
0 1 1 1
0 0 0 0
0 1 1 0
0 1 1 0
樣例輸出:
11
本題就是利用BFS的經典題目,首先來分析這個問題:
查找空間:所有(0,0,0)到點(A-1,B-1,C-1)合法的行走路徑
查找目標:找到通往出口的最短路徑
查找方法:將人物的三維位置座標以及所花費的時間作爲一個四元組(x,y,z,t),這就把查找所有路徑轉化成了對狀態的搜索,即查找(A-1,B-1,C-1,t),對於具體的某一狀態,可以通過擴展狀態來遍歷查找所有之後的狀態,比如(x,y,z,t)在沒有障礙物的情況下可以擴展爲(x+1,y,z,t+1)(x-1,y,z,t+1)(x,y+1,z,t+1)(x,y-1,z,t+1)(x,y,z+1,t+1)(x,y,z-1,t+1)這六個狀態,所以我們可以想象從(0,0,0,0)擴展到末狀態的情形。
這種擴展的形式抽象成數據結構就是樹,由(0,0,0,0) 爲根節點,在理想狀態下可以生出6個孩子,分別爲(x+1,y,z,t+1)(x-1,y,z,t+1)(x,y+1,z,t+1)(x,y-1,z,t+1)(x,y,z+1,t+1)(x,y,z-1,t+1),而這6個孩子又有機會擴展生出6*6個,如此延續,而我們的任務就是按層次遍歷這棵樹,這樣就可以儘可能早的找到目標狀態。
對於擴展,我們需要運用一些技巧來剪支,比如遇到障礙的支路需要刪除,之前訪問的過的位置在後來訪問時必然不可能是最短路徑的組成部分,這也可刪除,這可以用一個bool類型的數組來記錄其是否已經進行過
#include <stdio.h>
#include <queue>
using namespace std;
bool mark[50][50][50] ;
int maze[50][50][50] ;
struct Pos
{
int x,y,z ;
int t ;
};
queue<Pos> Q ;
int go[][3] =
{
1 , 0 , 0 ,
-1 , 0 , 0 ,
0 , 1 , 0 ,
0 ,-1 , 0 ,
0 , 0 , 1 ,
0 , 0 ,-1
};
int BFS(int a , int b ,int c)
{
while(Q.empty() == false)
{
Pos now = Q.front();
Q.pop();
for(int i = 0 ; i < 6 ; i++)
{
int nx = now.x + go[i][0];
int ny = now.y + go[i][1];
int nz = now.z + go[i][2];
//如果跑到範圍外面去了
if(nx < 0 || ny < 0 || nz < 0 || nx >= a || ny >= b || nz >= z) continue ;
//如果有障礙物
if(maze[nx][ny][nz] == 1) continue ;
//如果之前已經來過這個點了
if(mark[nx][ny][nz] == true) continue ;
Pos temp ;
temp.x = nx ;
temp.y = ny ;
temp.z = nz ;
temp.t = now.t + 1 ;
Q.push(temp);
mark[nx][ny][nz] = true ;
if(nx == a-1 && ny == b-1 && nz == c-1)
return temp.t ;
}
}
return -1 ;
}
int main()
{
int T ;
scanf("%d",&T);
while(T--)
{
int a,b,c,t ;
scanf("%d%d%d%d",&a,&b,&c,&t);
for(int i = 0 ; i < a ; i++)
{
for(int j = 0 ; j < b ; j++)
{
for(int k = 0 ; k < c ; k++ )
{
scanf("%d",&maze[i][j][k]);
mark[i][j][k] = false ;
}
}
}
//由於Q開始不一定爲空,所以要置空
while(Q.empty() == false) Q.pop();
mark[0][0][0] = true ;
Pos start ;
start.x = start.y = start.z =start.t = 0 ;
Q.push(start);
int ans = BFS(a,b,c);
if(ans <= t) printf("%d\n",ans);
else printf("-1\n");
}
return 0 ;
}
- 推箱子
那麼對於推箱子,思考方式也類似,只不過有一點需要注意,由於推箱子裏有兩個重要的元素,人和箱子,所以要mark人和箱子的座標,mark[x1][y1][x2][y2],一開始只mark[x1][y1]了人的座標信息這樣會導致錯誤。
#include <iostream>
#include <queue>
using namespace std ;
bool mark[8][8][8][8] ;
char game[8][8];
int go[][2] =
{
1 , 0 ,
-1, 0 ,
0 , 1 ,
0 ,-1
};
struct Pos
{
int x1 , y1 ,x2 ,y2 ;
int t ;
};
queue<Pos> Q ;
int BFS(int N , int M)
{
while(Q.empty() == false)
{
Pos now = Q.front();
Q.pop();
for(int i = 0 ; i < 4 ; i++)
{
int nx = now.x1 + go[i][0] ;
int ny = now.y1 + go[i][1] ;
if(nx < 0 || nx >= N || ny < 0 || ny >= M ) continue ;
if(game[nx][ny] == '#' ) continue ;
//到這裏說明人可以走到這個點,下面就判斷人是否和箱子有重合
if( nx == now.x2 && ny == now.y2)
{
//推算箱子的座標
int nxx = nx + go[i][0] ;
int nyy = ny + go[i][1] ;
//如果1 越界 2 障礙 3 已訪問
if( nxx < 0 || nxx >= N || nyy < 0 || nyy >= M) continue ;
if( game[nxx][nyy] == '#' ) continue ;
if( mark[nx][ny][nxx][nyy] == true ) continue ;
//否則
if( game[nxx][nyy] == '@') return now.t + 1 ;
Pos temp ;
temp.x1 = nx ;
temp.y1 = ny ;
temp.x2 = nxx ;
temp.y2 = nyy ;
temp.t = now.t + 1 ;
Q.push(temp);
mark[nx][ny][nxx][nyy] = true ;
}
//人沒有接觸到箱子
else
{
int nxx = now.x2 ;
int nyy = now.y2 ;
if( mark[nx][ny][nxx][nyy] == true ) continue ;
Pos temp ;
temp.x1 = nx ;
temp.y1 = ny ;
temp.x2 = nxx ;
temp.y2 = nyy ;
temp.t = now.t + 1 ;
mark[nx][ny][nxx][nyy] = true ;
Q.push(temp);
}
}
}
return -1 ;
}
int main()
{
for(int i = 0 ; i < 8 ; i++)
for(int j = 0 ; j < 8 ; j++)
for(int k = 0 ; k < 8 ; k++)
for(int l = 0 ; l < 8 ; l++)
mark[i][j][k][l] = false;
int N , M ,px , py , qx , qy;
cin>> N >> M ;
for(int i = 0 ; i < N ; i++)
{
for(int j = 0 ; j < M ; j++)
{
cin >> game[i][j] ;
//記錄人的位置
if(game[i][j] == 'X')
{
px = i ;
py = j ;
}
//記錄箱子的位置
else if(game[i][j] == '*')
{
qx = i ;
qy = j ;
}
}
}
//初始狀態設置true
mark[px][py][qx][qy] = true ;
Pos start ;
start.x1 = px ;
start.y1 = py ;
start.x2 = qx ;
start.y2 = qy ;
start.t = 0 ;
while(Q.empty() == false ) Q.pop();
Q.push(start);
int ans = BFS(N,M);
cout << ans ;
return 0 ;
}