链接:https://www.nowcoder.com/questionTerminal/4284c8f466814870bae7799a07d49ec8
来源:牛客网
小易来到了一条石板路前,每块石板上从1挨着编号为:1、2、3.......
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板
这条石板路要根据特殊的规则才能前进:对于小易当前所在的编号为K的 石板,小易单次只能往前跳K的一个约数(不含1和K)步,即跳到K+X(X为K的一个非1和本身的约数)的位置。 小易当前处在编号为N的石板,他想跳到编号恰好为M的石板去,小易想知道最少需要跳跃几次可以到达。
例如:
N = 4,M = 24:
4->6->8->12->18->24
于是小易最少需要跳跃5次,就可以从4号石板跳到24号石板
输入描述: 输入为一行,有两个整数N,M,以空格隔开。 (4 ≤ N ≤ 100000) (N ≤ M ≤ 100000)
输出描述: 输出小易最少需要跳跃的步数,如果不能到达输出-1
思路:
- res数组中存储着从起点到index位置所需最小步数(每部步长均为该步起点的约数),默认均为不可达(INT_MAX);
- 从起点(i = N)开始遍历,如果当前位置不可达,直接进入下一次循环,表示该位置不可用;
- 否则,计算从位置i走,可选的步长有哪些(vector v),遍历v,更新从位置i走一步(步长为it)可到达的位置的res数组对应值,即从起点到位置i+it的当前最少步数。(分位置i+it原本可达和不可达两种情况,详细论述见代码中注释)
- 最终,res数组中存储着从位置N到[N~M]间每个位置的最少步数的确定值
#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;
void func(int x, vector<int>& v) //求x除1和其本身的约数
{
int ret = 0;
for(int i = 2; i*i <= x; ++i)
{
if(x % i == 0)
{
v.push_back(i);
if(x / i != i)
v.push_back(x / i);
}
}
}
int main()
{
int N, M;
while(cin >> N && cin >> M)
{
vector<int> res(M+1, INT_MAX); //记录从N到该位置(cur_index)所需步数,INT_MAX为不可达,初始值为INT_MAX
res[N] = 0;
for(int i = N; i < M; ++i)
{
if(res[i] == INT_MAX)
continue;
//以下,位置i可达
vector<int> v;
func(i, v); //v中为i的约数,即从位置i走,可选的步长
for(const auto it : v)
{
if(i + it <= M && res[i+it] != INT_MAX)
//从位置i走it步,不超出终点M;
//位置i+it原本可到达,更新为较小的
res[i+it] = min(res[i+it], res[i]+1);
//从位置i走,一次it步长可到达位置i+it,
//即从N到达位置i+it,所以一个可选方案是走ret[i]+1步,与原本的值保留较小的
else if(i + it <= M)
//位置i+it本不可达,现在已知从位置i走一次步长为it可到达该位置,因此更新为ret[i]+1
res[i+it] = res[i]+1;
}
}
if(res[M] == INT_MAX) //每次步长为约数,不可达
cout << -1 << endl;
else //可达
cout << res[M] << endl;
}
return 0;
}