【问题标题】:Find minimum steps required to reach n找到达到 n 所需的最小步数
【发布时间】:2020-05-26 16:51:33
【问题描述】:

我正在尝试解决如下动态规划问题,但无法解决。

给你一个原始计算器,它可以对当前数字 ???? 执行以下三个操作:乘 ????乘以 2,乘 ???? 3,或加 1 到????。你的目标是给定一个正整数???从数字 1 开始

我在 stackoverflow 本身上找到了解决方案,但无法理解发生了什么。

我听说每个 DP 问题都可以通过创建我试图做但不知道哪里出错的矩阵来解决。下面创建的表格显示了从 1 到达 n 所需的步骤数,最初我将值设为无穷大。

i / j           0           1             2            3                4              5
plus 1          0           1             2            3                4              5
multiple by 2   0           infinity      2            infinity         3             infinity
multiple by 3   0           infinity      infinity     2                infinity      infinity

我正在尝试用 Python 解决这个问题。 谁能帮帮我。

我找到了如下解决方案,但无法准确理解发生了什么:

import math
target = int(input())

def optVal(target, cache):
    result = [1] * cache[-1]  # 1
    for i in range(1, cache[-1]): # 2
        result[-i] = target  # 3
        if cache[target-1] == cache[target] - 1:  # 4
            target -= 1
        elif target % 2 == 0 and (cache[target // 2] == cache[target] - 1):  # 5
            target //= 2
        else:  # 6 # target % 3 == 0 and (cache[target // 3] == cache[target] - 1):
            target //= 3
    return result

cache = [0] + [math.inf] * target  # 1
for i in range(1, len(cache)):  # 2
    temp1 = math.inf
    temp2 = math.inf
    temp3 = math.inf

    temp1 = cache[i - 1] + 1
    if i % 2 == 0:
        temp2 = cache[i // 2] + 1
    if i % 3 == 0:
        temp3 = cache[i // 3] + 1

    cache[i] = min(temp1, temp2, temp3)

print('Minimum operation: ', cache[target] - 1)
finalLst = optVal(target, cache)
print(' '.join([str(x) for x in finalLst]))

Input: 
5
Output:
3
1245

【问题讨论】:

标签: python algorithm dynamic-programming


【解决方案1】:

此算法分为两部分。第一个在main中,第二个在optVal函数中。

第一部分构建cache 列表,其中cache[i] 包含从0 到达i 所需的最小步骤数,在每个步骤中应用三个可能的操作之一:+1*2*3。此列表是您阅读的矩阵的一维情况。

在计算cache[i] 时,所有低于i 的索引都已计算完毕。可以通过三种可能的方式到达i,因此最多需要检查i 的三个可能来源,即cache 的元素:i-1i//2i//3,但是i//2 仅当i 为偶数,i//3 仅当i 可以被3整除。cache 的这些元素进行比较,获胜者的内容,加1(因为额外的到达i的步骤),存储在cache中。通过将0 放入cache[0] 来引导此过程。最后,cache[target] 将包含从0 开始到达target 的最少步骤(这比从1 开始的步骤多1,这就是问题的陈述方式——请注意,您只能应用+1 操作从0 移出)。

现在,如果我编写了代码,我可能会存储每个 cache[i] 的“父”或“获胜操作”以及到达那里的步骤数(顺便说一句,那些 math.inf 不是真的需要,因为到达i 的步骤总是有限的,因为+1 操作。)作者的方法是从每个可能的父母(最多3个)的内容中推断出这个信息cache[i] 需要检查。在这两种情况下,“祖先”链都必须从cache[target]开始向后重构,这就是optVal()中发生的情况。

optVal() 中,target 在每次迭代时都会更改(有点令人困惑),因为在每次迭代中,您拥有的信息是达到某个目标数所需的最少步数。知道这一点后,您可以查看 1、2 或 3 个可能的父项,以检查哪一个包含的步骤数减去 1。通过测试的那个是实际的父项,因此您可以继续向后构建链,替换 @987654361 @与父母。

【讨论】:

  • 很好的解释,终于看懂了。谢谢
【解决方案2】:

要解决这个 DP,您必须构建一个表,其中包含获得 n 所需的最小步骤数,如果一个两个或所有操作都可用。您将创建它从左到右,从上到下,即 1 到 n,将 1 加到 mul 3。随着您向下,更多的操作可用

单元格值仅取决于其上方的值(如果可用)和左侧的 atmax 3 个值,例如。对于 (n = 6),(mul 3) 单元格将仅取决于 (n = 6),(mul 2)(n = 2)(mul 3)(n = 3)(mul 3)(n = 5)(mul 3)。然后,您将比较这些值,并且在操作后以较小者为准,您将输入该值,因此您将比较 (n = 2)(mul 3) + 1(n = 3)(mul 3) + 1(n = 5)(mul 3) + 1(n = 6)(mul 2) 的值,然后以较小者为准那个值

由于 n = 1,第一列的所有值都为零

对于 n = 2,其值将取决于 n = 1 的值。您可以“加 1”或“乘以 2”(1 步),两者都有效。所以这一列的所有值都是 0 + 1 = 1

对于 n = 3,它的值将取决于 n = 1 的值(因为 1 = 3 的 1/3)和 n = 2。如果你只能“加 1”或“乘以 2”,那么你将选择将 1 添加到 n = 2,因此总步数 1+1 = 2。但是,如果您还可以乘以 3,则只需一步,因此 0 + 1 = 1。由于 1

对于 n = 4,它将取决于 n = 3(加 1)和 n = 2(mul 2)。所以值将是 3, 2, 2

对于 n = 5,它将取决于 n = 4(加 1)。所以值将是 4, 3, 3

所以达到 n = 5 的最小步数是 3

决赛桌:

    1  2  3  4  5
add 1  0  1  2  3  4
mul 2  0  1  2  2  3
mul 3  0  1  1  2  3

【讨论】:

    【解决方案3】:
    #include <bits/stdc++.h>
    
    using namespace std;
    int rec(vector<int> &dp,int n)
    {
        if(n==1) return 0;
        if(dp[n]!=INT_MAX) return dp[n];
        return dp[n]=min({1+rec(dp,n-1),(n%2==0)?1+rec(dp,n/2):INT_MAX,(n%3==0)?1+rec(dp,n/3):INT_MAX});
    }
    string genseq(vector<int> &dp, int n){
        string res="";
        while(n>1)
        {
            res=to_string(n)+" "+res;
            if(dp[n-1]==(dp[n]-1)) n--;
            else if(n%2==0&&( dp[n/2]==dp[n]-1)) n/=2;
            else if(n%3==0&&( dp[n/3]==dp[n]-1)) n/=3;
        }
        return "1 "+res;
    }
    int main()
    {
        int n;
        cin>>n;
        vector<int> dp(n+1,INT_MAX);
        dp[0]=0;
        dp[1]=0;
        std::cout << rec(dp,n) << std::endl;
        std::cout << genseq(dp,n) << std::endl;
        return 0;
    }
    

    【讨论】:

    • 欢迎来到 SO!您误解了这个问题:操作人员没有要求另一种解决方案,而是要求对他所展示的解决方案进行解释。另外:这个问题有一个 Python 标签,而不是 C++。你应该删除你的答案。
    猜你喜欢
    • 2019-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-10
    • 1970-01-01
    • 1970-01-01
    • 2019-02-20
    • 2012-01-06
    相关资源
    最近更新 更多