跳石板

题目描述

小易来到了一条石板路前,每块石板上从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号石板。

思路:动态规划
步骤:
    首先要对每一步的所有约数进行记录;
    再判断当前位置+约数:
        1. 是否越界;
        2. 其位置是否走过;
        3. 如果走过,当前的步数是否是更优解。
    循环遍历,直到终点。

本题最主要的处理就是更新每个点上的最小步数,我们通过动态方程:

steps[cur+div] = min(steps[cur]+1,steps[cur+div]) // cur当前位置,div约数

上代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <math.h>

using namespace std;

int main()
{
	int n, m;
	cin >> n >> m;

	vector<int> step(m + 1, 0);

	int index = n;
	step[index] = 1; // step记录到达该位置最小步
	vector<int> div; // div记录当前下标的所有约数

	for (int cur = index; cur < m; cur++){

		// 排除上次位置到不了的位置
		if (step[cur] == 0){
			continue;
		}

		// 求出当前位置的所有约数
		for (int i = 2; i <= sqrt(cur); i++){
			// i是约数,需要处理
			if (cur % i == 0){

				div.push_back(i);
				
				// 表示约数不是某个数的平方项,需要记录大于开方项的值
				if (cur / i != i){
					div.push_back(cur / i);
				}
			}
		}

		for (int j = 0; j < div.size(); j++){
			// 处理下一次位置之前没走过
			if ((cur + div[j]) <= m && step[cur + div[j]] == 0){
				step[cur + div[j]] = step[cur] + 1;
			}
			// 处理下一次位置之前走过,取它们最小
			else if ((cur + div[j]) <= m && step[cur + div[j]] != 0){
				step[cur + div[j]] = min(step[cur] + 1, step[cur + div[j]]);
			}
		}
	}
		if (step[m] == 0){
			cout << -1;
		}
		else
			cout << step[m]-1;
	
	return 0;
}

通过运行发现其运算复杂度过大,部分编译不通过。

我们再来看代码,发现每次求出约数时可以直接进行下步:判断是否更新step的操作,所以我们可以将其合并。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std;

int main()
{
	int n, m;
	cin >> n >> m;
	vector<int> step(m + 1, 0);
	int index = n;
	step[index] = 1; // step记录到达该位置最小步
	
	for (int cur = index; cur <= m; cur++){
		// 排除上次位置到不了的位置
		if (step[cur] == 0){
			continue;
		}
		// 求出当前位置的所有约数
		for (int i = 2; i <= sqrt(cur); i++){
			// i是约数,需要处理
			if (cur % i == 0){
				// 这里不做存入约数的操作,直接进行更新操作
				if ((cur + i) <= m && step[cur + i] == 0){
					step[cur + i] = step[cur] + 1;
				}
				else if ((cur + i) <= m && step[cur + i] != 0){
					step[cur + i] = min(step[cur] + 1, step[cur + i]);
				}
				
				// 表示约数不是某个数的平方项,需要记录大于开方项的值
				if (cur / i != i){
					if ((cur + cur / i) <= m && step[cur + cur / i] == 0){
						step[cur + cur / i] = step[cur] + 1;
					}
					else if ((cur + cur / i) <= m && step[cur + cur / i] != 0){
						step[cur + cur / i] = min(step[cur] + 1, step[cur + cur / i]);
					}
				}
			}
		}
	}
		if (step[m] == 0){
			return -1;
		}
		else
			cout << step[m]-1;
	return 0;
}

  改完之后代码就可以AC,最后其实我们可以将判断是否更新的操作封装成一个函数,这样看起来也更加简洁。在这就不贴代码。

如果还有更好方法,希望提出、交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值