【万题详解】洛谷P1135奇怪的电梯

题目

链接——题目在这里!!!

题目背景

感谢 @yummy 提供的一些数据。

题目描述

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 i 层楼(1≤i≤N)上有一个数字 Ki​(0≤Ki​≤N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3,3,1,2,53,3,1,2,5 代表了 Ki​(K1​=3,K2​=3,……),从 1 楼开始。在 1 楼,按“上”可以到 4楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 A 楼到 B 楼至少要按几次按钮呢?

输入格式

共二行。

第一行为三个用空格隔开的正整数,表示N,A,B(1≤N≤200,1≤A,B≤N)。

第二行为 N 个用空格隔开的非负整数,表示 Ki​。

输出格式

一行,即最少按键次数,若无法到达,则输出 -1

输入输出样例

输入 #1

5 1 5
3 3 1 2 5

输出 #1

3

说明/提示

对于 100%的数据,1≤N≤200,1≤A,B≤N,0≤Ki​≤N。

本题共 16个测试点,前 15个每个测试点 6分,最后一个测试点 10分。

解题思路

以下我们将按一次按钮视作当前时刻加 1。

对于一个点,我们明显只会关心更早到达这个点的时刻,而之后到达的我们都不关心,因为更晚到达的情况拓展出去的状态肯定是会更劣的。

所以我们希望更早地拓展时间最小的状态,以此来保证我到达某个点时的时间一定到达它的最短时间。

考虑到每个边的代价都是 1,那么我们可以果断使用 bfs 来解决这一问题。

首先,我们每次贪心地选择当前时间最小的点向外拓展,如果拓展到了没有到达过的点那么就更新这个点的答案,由于我们取的是当前时间最小的点,所以它拓展到的点一定是当前能拓展到的点中到达时刻最小的点,而且在它之后被拓展到的点的到达时间都不会比这个点小,也就是说到达时间小的点一定会更先被拓展,我们按照访问到的顺序把点存在队列里就行,每次取出队列的第一个拓展。

所以就这样直接 BFS 就行,这道题 DFS 或 BFS 均可,但是 BFS 相对更加好写(无需剪枝)。

BFS 广度优先搜索是使用队列(queue,先进先出)来实现,整个过程也可以看做一个倒立的树形:

  1. 把根节点放到队列的末尾。
  2. 每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾 (不需要排序,因为默认每次增加权重相同)。并把这个元素记为它下一级元素的前驱。
  3. 找到所要找的元素时结束程序。
  4. 如果遍历整个树还没有找到,结束程序。

最坏情况 O(n)。

AC(DFS)

#include 
using namespace std;
#define M 200
int arr[M + 5], vis[M + 5] = { 0 };
void dfs(int i, int a, int n) {
	if (vis[a] <= i) return;
	vis[a] = i;
	if (a - arr[a] >= 1) {
		dfs(i + 1, a - arr[a], n);
	}
	if (a + arr[a] <= n) {
		dfs(i + 1, a + arr[a], n);
	}
	return;
}
int main() {
	int n, a, b;
	cin >> n >> a >> b;
	for (int i = 1; i <= n; i++) {
		cin >> arr[i];
	}
	for (int i = 1; i <= n; i++) {
		vis[i] = 201;
	}
	dfs(0, a, n);
	if (vis[b] == 201) cout << "-1";
	else cout << vis[b];
	return 0;
}

AC(BFS) 

#include
using namespace std;
int a,b,x[500]/*电梯上的数字*/;
bool vis[500];
int q,p; //头、尾指针 
int N;
struct node
{
	int floor,dep;
}Q[1000];
int bfs() //广搜 
{
	if(a==b) return 0; //如果开始层等于结束层,直接返回0步 
	int ans,top,k;
	Q[1]={a,0};
	q=1;p=2;
	while(q=1&&k<=N&&!vis[k])
			{
				vis[k]=true;
				if(k==b) return ans+1;  //到达终点,返回答案 
				Q[p]={k,ans+1}; //入队列 
				p++;
			}
		}
		q++;
	}
	return -1;
}
int main()
{
	cin>>N>>a>>b;
	for(int i=1;i<=N;i++) cin>>x[i];
	cout<

AC(另一种)

#include
using namespace std;
int f[205][205],n,a,b,k[205]; 
int main(){
	cin>>n>>a>>b;
	for(int i=1;i<=n;i++)cin>>k[i];
	f[0][a]=1;
	for(int i=0;i=1)f[i+1][j-k[j]]=f[i][j];
		}
	}
	for(int i=0;i<=n;i++){
		if(f[i][b]){
			cout<

你可能感兴趣的:(算法,c++,宽度优先,深度优先)