力扣650. 只有两个键的键盘 递归法、素数分解法与动态规划法多种解法!

最初在一个记事本上只有一个字符 ‘A’。你每次可以对这个记事本进行两种操作:

Copy All (复制全部) : 你可以复制这个记事本中的所有字符(部分的复制是不允许的)。
Paste (粘贴) : 你可以粘贴你上一次复制的字符。
给定一个数字 n 。你需要使用最少的操作次数,在记事本中打印出恰好 n 个 ‘A’。输出能够打印出 n 个 ‘A’ 的最少操作次数。

示例 1:
输入: 3
输出: 3
解释:
最初, 我们只有一个字符 ‘A’。
第 1 步, 我们使用 Copy All 操作。
第 2 步, 我们使用 Paste 操作来获得 ‘AA’。
第 3 步, 我们使用 Paste 操作来获得 ‘AAA’。

说明:
n 的取值范围是 [1, 1000] 。

解法一递归思路(数学法):
首先显然,如果n是素数,我们只能通过复制一次加粘贴n-1次得到,因为题目已知只能复制已有的全部字符。
如果其是合数,我们先找到其最大因子i:n=i*(n/i),则我们需要先得到i个字符,再复制一次,粘贴(n/i)-1次即可得到n个字符,为什么要这样做呢?比如18=2×9,是复制一次粘贴17次少还是复制一次粘贴一次得到AA,再复制一次再粘贴8次一共11次少? 显然11次少,所以最优最少的求法就是将n尽可能的分解到不能再分解的地步。
所以有:
minSteps(n)=minSteps(i)+n/i
得到递归公式,代码显然得出:

int minSteps(int n) {
	if (n == 1) {//默认原有一个字符,不需要任何操作
		return 0;
	}
	for (int i = n / 2; i > 1; i--) {//从n/2开始寻找n的最大因子
		if (n % i == 0) {
			return minSteps(i) + n / i;
		}
	}
	return n;//如果n为素数则需要n次操作
}

在这里插入图片描述
解法二素数分解法:
借由解法一的递归思路,我们带入实例:
比如n=48 则n=24×2
而24=12×2 12=6×2 6=3×2
所以minSteps(48) =2+2+2+2+minSteps(3)
又因为3是素数 所以minSteps(3)=3
所以我们可知,此题的求解就是将给定数字分解为素因子,并求和。代码显然:

int minSteps(int n) {
	int all = 0;
	for (int i = 2; i <= n; i++) {//从2开始不断寻找n的素因子
		while (n % i == 0) {//如果i是n的素因子,则需要一次性将n中该素因子除尽
			all += i;
			n /= i;
		}
	}
	return all;
}

在这里插入图片描述

解法三动态规划:
设置dp[i]的含义:得到i需要的最少次数
则dp[1]=0,dp[0]不用,题目所求即为求dp[n]
n为素数,则dp[n]=n
n为合数,若n可以被i整除,则说明可以通过复制粘贴i得到n个A,复制粘贴次数为1+(n/i-1)=n/i
所以我们不断的搜查每个数y的因子并求得对应因子所得出的次数,求出其对应的最终dp[y]循环得到dp[n]。
则得到递推公式:
dp[n]=min(dp[n],dp[i]+n/i)。
递推公式写出,则代码立即有:

int minSteps(int n) {
	vector<int> dp(n + 1);
	dp[1] = 0;
	for (int i = 2; i <= n; i++) {
		dp[i] = i;//i为i的最大次数,当i为素数时dp[i]=i
		for (int j = 2; j < i/2; j++)//优化算法:从2往上查找因数只需要查到其平方根大小即可
		{
			if (i % j == 0)
				dp[i] = min(dp[j] + i/j,dp[i]);
		}//不断循环求出其最佳值
	}
	return dp[n];
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值