问题描述
试题编号: | 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];
}