数字游戏2

在做一道hihocoder上的题目的时候,开始的时候一时间没有想出来这里写博客理一下思路。
题目内容:
小 Hi 有一个数字 k,小 Hi 可以对他进行多次变换:每次变换选择 k 的一个大于 1 的约数 d,然后将 k 变成 k/d
现在小 Hi 想将一个数字变成 1,求操作的方案数。由于方案数可能过大,你只需要输出方案数对 10的9次方+7 取模后的值。
例如对于k=10,有三种方案:10->5->1,10->1,10->2->1。
输入: 一个正整数 k 1 ≤ k ≤ 106
输出: 输出将 k 变成 1 的操作序列的方案数
样例输入:
10
样例输出:
3

最开始看到题目的时候,觉得该题目中蕴含了一种递归的关系:
以10举例
10->5->1
10->1
10->2->1
一个数k的操作序列方案数实际上等于 他的所有不大于本身的约数(这里的约数包含1)的方案数相加,这里存在一种递归子问题的结构
以10举例的化:
10的操作方案数 =  2的操作方案数 + 5的操作方案数 + 1的操作方案数
详细的描述:
10->[5->1]
10->[1]
10->[2->1]
用方括号括起来的可以看成是子问题,由于1的操作方案数为12的操作方案数也为13的操作方案数也为1,所以10的操作方案数为3
从上面的分析中,我们可以看出其中包含的子问题特性和递推关系,所以可以采用动态规划的方法来解决
实现方案1:
import java.util.Arrays;
int k = 10; //假设输入的k是
long mod = 1000000007; //取余数所用的模
long[] dp = new long[k + 1];
Arrays.fill(dp, 0);
dp[1] = 1;
for(int i = 1; i <= k; i++) {
    for(int num = 2 * i; num <= k; num += i) {  //求所有i的倍数,因为这些数为i的倍数,所以其方案数需要加上i的方案数
        dp[num] = (dp[num] + dp[i]) % mod;
    }
}
System.out.println(dp[k]);
3
实现方案2:
import java.util.Arrays;
int k = 10; //假设输入的k是
long mod = 1000000007; //取余数所用的模
long[] dp = new long[k + 1];
Arrays.fill(dp, 0);
dp[1] = 1;
for(int i = 2; i <= k; i++) {
    //该dp过程去寻找i的所有约数,将这些约数的对应的子问题的和加起来就是它本身的解
    for(int tmp = 1; tmp * tmp <= i; tmp += 1) {
        if(i % tmp != 0) continue; 
        if(i / tmp == tmp || tmp == 1) {
            dp[i] = (dp[i] + dp[tmp]) % mod;  // 如果约数的平方为i 或tmp值为1的时候,则只计算一次,注意该情况
        } else {
            dp[i] = (dp[i] + dp[tmp]) % mod;
            dp[i] = (dp[i] + dp[i / tmp]) % mod;
        }
    }
}
System.out.println(dp[k]);
3

上面两种实现方案虽然都是动态规划(dp),但是一个用当前值往后递推,将当前知道的信息叠加都后面的待求项去,另一种方法是,在要求解某数的解决方案数的时候,去求它的所有约数(不包括本身),然后将他们加起来做自己的值。
明显两种方案都能解决问题,但是方案一的运行效率明显要高很多,因为方案一在循环的时候不会有无效的递推,方案二中,在求取i的时候,对tmp值的尝试会出现失败的情况,使其算法复杂度高于方案一的。
但是两种递推的思维方式还是要掌握的,一种是从前往后判断自己与谁有关系,将自己的信息使用到与自己有关的对象上,一种是从后往前去寻找自己与谁有关系,将他们的信息使用到自己上。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值