問題描述
試題編號: | 201604-4 |
試題名稱: | 遊戲 |
時間限制: | 1.0s |
內存限制: | 256.0MB |
問題描述: |
問題描述 小明在玩一個電腦遊戲,遊戲在一個n×m的方格圖上進行,小明控制的角色開始的時候站在第一行第一列,目標是前往第n行第m列。 輸入格式 輸入的第一行包含三個整數n, m, t,用一個空格分隔,表示方格圖的行數n、列數m,以及方格圖中有危險的方格數量。 輸出格式 輸出一個整數,表示小明最快經過幾個時間單位可以過關。輸入數據保證小明一定可以過關。 樣例輸入 3 3 3 樣例輸出 6 樣例說明 第2行第1列時刻1是危險的,因此第一步必須走到第1行第2列。 評測用例規模與約定 前30%的評測用例滿足:0 < n, m ≤ 10,0 ≤ t < 99。 |
一開始用的是深搜,也料到了會超時的。
dfs20分代碼:
//DFS
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
using namespace std;
struct node
{
int start,end;
node(int s=0,int e=0)
{
start=s;end=e;
}
}map[101][101];
bool islegal(node a,int time)//time是否處於危險時刻
{
if(time>=a.start&&time<=a.end) return 0;
return 1;
}
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};//上下左右
int n,m,min_time=300;//min_time爲最小時間
void dfs(int i,int j,int step)
{
if(i==n&&j==m)//到達終點
{
min_time=min(min_time,step);
return;
}
if(step>=min_time) return;
if(step+abs(i-n)+abs(j-m)>=min_time) return;//曼哈頓距離剪枝
for(int p=0;p<4;p++)
{
int x=i+dx[p],y=j+dy[p];
if(x<1||x>n||y<1||y>m) continue;//越界
if(islegal(map[x][y],step+1))
dfs(x,y,step+1);
}
}
int main()
{
int t;
cin>>n>>m>>t;
for(int i=1;i<=t;i++)
{
int r,c;
cin>>r>>c;
cin>>map[r][c].start>>map[r][c].end;
}
dfs(1,1,0);
cout<<min_time;
}
後來用廣度優先搜索(寬度優先搜索)。
廣度優先搜索按照距開始狀態由近及遠的順序進行搜索,因此可以很容易地用來求最短路徑、最少操作之類問題的答案。
寬度優先搜索中,只要將已經訪問過的狀態用標記管理起來,就可以很好地做到由近及遠的搜索。
在這題用三維數組isvisit[101][101][301]對狀態進行標記,初始化爲0,isvisit[i][j][k]爲1表示在時刻k,第i行j列是無法訪問的,則這一時刻要麼是處於危險時期,要麼是先前訪問過。注意第三維的座標,從左上角到右下角最多走200步,時間範圍a,b<=100。
用d[i][j]表示從起點到i行j列的最小時間。
以下橙色字體摘自《挑戰程序設計競賽(第二版)》
寬度優先搜索與深度搜索一樣,都會生成所有能夠遍歷到的狀態,因此需要對所有狀態進行處理時使用寬度優先搜索也是可以的。但是遞歸函數可以很簡短地編寫,而且狀態的管理也更簡單,所以大多數情況下還是使用深度優先搜索實現。反之,在求取最短路徑時深度優先搜索需要反覆經過同樣的狀態,所以此時還是使用寬度優先搜索爲好。
寬度優先搜索會把狀態逐個加入隊列,因此通常需要與狀態數成正比的內存空間。反之,深度優先搜索是與最大遞歸深度成正比的。一般與狀態數相比,遞歸的深度並不會太大,所以可以認爲深度優先搜索更加節省內存。
上圖印證了深度優先搜索更加節省內存,但廣搜不必遞歸,節省了很多時間。
AC代碼:
//BFS
#include<iostream>
#include<sstream>
#include<algorithm>
#include<string>
#include<cstring>
#include<iomanip>
#include<vector>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#define P pair<int,int>
using namespace std;
struct node
{
int start,end;//危險的開始時刻、結束時刻
node(int s=0,int e=0)
{
start=s;end=e;
}
}map[101][101];
int dx[4]={0,0,-1,1},dy[4]={-1,1,0,0};//上下左右
int n,m,d[101][101];//d[i][j]表示從起點到i行j列的最小時間
bool isvisit[101][101][301];//visited[i][j][k]爲1表示在時刻k,第i行j列不能訪問
void bfs()
{
queue<P> que;
memset(d,0,sizeof(d));
que.push(make_pair(1,1));
//不斷循環直到隊列爲空
while(!que.empty())
{
//從隊列的最前端取出元素
P p=que.front();que.pop();
//如果取出的狀態已經是終點,則結束搜索
if(p.first==n&&p.second==m) break;
for(int i=0;i<4;i++)
{
int x=p.first+dx[i],y=p.second+dy[i];
if(x<1||x>n||y<1||y>m) continue;//超出邊界
int time=d[p.first][p.second]+1;
if(isvisit[x][y][time]) continue;//無法訪問
isvisit[x][y][time]=1;
que.push(make_pair(x,y));
d[x][y]=time;
}
}
}
int main()
{
int t;
cin>>n>>m>>t;
memset(isvisit,0,sizeof(isvisit));
for(int i=1;i<=t;i++)
{
int r,c,a,b;
cin>>r>>c>>a>>b;
map[r][c].start=a,map[r][c].end=b;
for(int j=a;j<=b;j++) isvisit[r][c][j]=1;
}
bfs();
cout<<d[n][m];
}