题目描述
呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 𝑖 层楼(1≤𝑖≤𝑁)上有一个数字 𝐾𝑖(0≤𝐾𝑖≤𝑁)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: 3,3,1,2,5 代表了 𝐾𝑖(𝐾1=3,𝐾2=3,……),从 1楼开始。在 1 楼,按“上”可以到 4 楼,按“下”是不起作用的,因为没有 −2 楼。那么,从 𝐴楼到 𝐵 楼至少要按几次按钮呢?
输入格式
共二行。
第一行为三个用空格隔开的正整数,表示 𝑁,𝐴,𝐵(1≤𝑁≤200,1≤𝐴,𝐵≤𝑁)。
第二行为 𝑁 个用空格隔开的非负整数,表示 𝐾𝑖。
输出格式
一行,即最少按键次数,若无法到达,则输出 -1
。
输入输出样例
输入
5 1 5 3 3 1 2 5
输出
3
说明/提示
对于 100%的数据,1≤𝑁≤200,1≤𝐴,𝐵≤𝑁,0≤𝐾𝑖≤𝑁。
算法设计
本题解题的思路用到一个优化减枝的方法。
如上图所示,从A结点到绿色结点可以有一条长度为2的路径,也可以有一条长度为3的路径。
假设我们要找一条从A点——>绿色结点——>其他的路径,假设绿色结点——>其他路径长为x,那么从A点——>绿色结点——>其他的路径可以为2+x和3+x,我们显而易见的可知2+x必定比3+x更短。于是,在实际的搜索过程中,当我们搜索到a到绿色结点的路径为3<已知的2时,我们可以不必继续往下搜索。
函数设计
我们定义一个数组to[ ]来存放每个楼层上标记的数字
int to[MAX_N + 5];//每个楼层标记的数
//主函数
scanf("%d%d%d", &n, &a, &b);
for (int i = 1; i <= n; i++)scanf("%d",to+i);
定义数组dis[b]存储从起点到b的已知最短距离。在主函数中,我们给它最大的初始值。
我们定义函数dfs(int k,int a,int n),其中k表示到达a点的步数。如果当前的步数k大于dis[ ]数组中记录的已知最少步数,那么不必继续向下搜索,直接return。否则将k的值赋给dis[ ]数组。并继续向左右移动。
void dfs(int k, int a,int n) {//到达a点用了k步
if (dis[a] <= k)return;
dis[a] = k;
if (a + to[a] <= n)dfs(k + 1, a + to[a], n);
if (a - to[a] >= 1)dfs(k + 1, a - to[a], n);
return;
}
最后输出dis[b]中的值,即为a到b的最短步数。但是,如果dis[b]中的值依然是初始值n-1,则表示a到b不可达,输出-1。最后整理得到完整代码如下:
#include <string>
#include<map>
#include<set>
#include<vector>
#include<iostream>
using namespace std;
#define MAX_N 200
int to[MAX_N + 5];//每个楼层标记的数
int dis[MAX_N + 5] = { 0 };//起点到每一个结点的已知最短距离
void dfs(int k, int a,int n) {//到达a点用了k步
if (dis[a] <= k)return;
dis[a] = k;
if (a + to[a] <= n)dfs(k + 1, a + to[a], n);
if (a - to[a] >= 1)dfs(k + 1, a - to[a], n);
return;
}
int main() {
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
for (int i = 1; i <= n; i++)scanf("%d",to+i);
for (int i = 1; i <= n; i++)dis[i] = n + 1;
dfs(0,a,n);
printf("%d", dis[b] == n + 1 ? -1 : dis[b]);
return 0;
}