如何将此递归函数转换为基于dp的解决方案?

时间:2019-12-26 12:57:08

标签: algorithm dynamic implementation

这是递归函数

def integerPartition(m, n):
    if(n==0):
        return 0
    if(m ==0):
        return 1
    if(m<0):
        return 0

    return integerPartition(m,n-1) + integerPartition(m-n,n)

这就是我在c ++中所做的

        // n -> no. of persons
        // m -> amount of money to be distributed
        // dp table of order (n+1)*(m+1) 
        long long int dp[n+1][m+1] ;
        //initializing values to 0
        for(i = 0; i<=n ; i++)
            for(j = 0; j<= m ; j++)
                dp[i][j] = 0;

        Print(n,m,dp);
        cout << "\n";
        //Case 1 - if there is no persons i.e n = 0 answer will be 0
        //Case 2 - if there is no money i.e. m = 0 there is only 1 way answer will be 1
        for ( i = 1; i<= n ; i++ )
            dp[i][0] = 1;
            dp[i][i] = 1;

        Print(n,m,dp);

        for ( i = 1; i<= n ; i++){
            for ( j = 1; j<= m ; j++){
                dp[i][j] = dp[i][j-1] ;

                if(i>=j){
                    dp[i][j] += dp[i-j][j];
                }
                // else if(i==j){
                //     dp[i][j] += 1;
                // }
            }
    }

但是我得到的答案与递归答案不匹配我不明白如果有人可以请我帮助我解决的问题我会很感激,因为我刚开始使用动态编程,所以我真的不能弄清楚

3 个答案:

答案 0 :(得分:1)

一些问题:

  • 您似乎在for循环中使用了非局部变量。这是一种不好的做法,并且可能导致难以调试的错误。代替 做for (int i = 1; ...等等。

  • dp[i][i] = 1;不是for循环的一部分。如果仅将i定义为for循环的局部变量,则将检测到此情况。 优良作法是始终对for循环的主体使用括号(也ifelse,...等),即使您只有一个括号也是如此 声明。

  • dp[i][i] = 1;也是一个错误的分配:integerPartition(i, i)总是返回1只是不正确。 对于较小的i值,但当i大于3时则不然。例如,integerPartition(4, 4)应该返回5。 只需删除此行即可。

  • 在最后一个嵌套的for循环中,您混合了dp数组中的行/列。请注意,您已经为n保留了第一维,为m保留了第二维,因此与参数顺序相反。 很好,但是您不会在此for循环中坚持该决定。您应该写dp[i][j-1]而不是dp[i-1][j],而不是dp[i-j][j] 写成dp[i][j-i]。因此if条件应作相应调整。

  • 您的版本中没有return语句,但是也许您只是忘了将其包含在问题中。它应该是 return dp[n][m];

这是更正的代码:

long long int dp[n+1][m+1];

for(int i = 0; i <=n; i++) {
    for(int j = 0; j <= m; j++) {
        dp[i][j] = 0;
    }
}

for (int i = 1; i <= n; i++) {
    dp[i][0] = 1;
}

for (int i = 1; i <= n; i++){
    for (int j = 1; j <= m ; j++) {
        dp[i][j] = dp[i-1][j];
        if (j >= i) {
            dp[i][j] += dp[i][j-i];
        }
    }
}

return dp[n][m];

答案 1 :(得分:0)

不确定技术上是DP,但是如果您的目标是要获得DP的好处,记忆可能是一种更好的方法。

这个想法由两部分组成:

  1. 在每次调用integerPartition时,在一个表中查找(您的dp会很好地执行)以查看该计算是否已经完成,如果已经完成,只需返回存储在表中的值即可。

  2. integerPartition返回值的任何点之前,将其存储在表中。

请注意,这意味着您无需尝试“旋转”原始代码-它会像原始代码一样继续进行,因此几乎可以保证您获得相同的结果,但无需进行太多不必要的重新计算(在额外存储空间的代码中。

答案 2 :(得分:0)

所以,根据您的代码注释, 我将假设您仅根据递归代码在n> 0且m = 0时才需要1,但是在dp代码中,您将它们互换了,即我升至n,j升至m 所以更新您的代码,尝试找出错误


        // n -> no. of persons
        // m -> amount of money to be distributed
        // dp table of order (n+1)*(m+1) 
        long long int dp[n+1][m+1] ;
        //initializing values to 0
        for(i = 0; i<=n ; i++)
            for(j = 0; j<= m ; j++)
                dp[i][j] = 0;

        Print(n,m,dp);
        cout << "\n";
        //Case 1 - if there is no persons i.e n = 0 answer will be 0
        //Case 2 - if there is no money i.e. m = 0 there is only 1 way answer will be 1

        for ( i = 1; i<= n; i++){
            dp[i][0] = 0;
        }

        for(int j = 1; j <= m; j++){
            dp[0][j] = 1;
        }

        Print(n,m,dp);

        for ( i = 1; i<= n ; i++){
            for ( j = 1; j<= m ; j++){
                dp[i][j] = dp[i][j-1] ;

                if(i>=j){
                    dp[i][j] += dp[i-j][j];
                }
                // else if(i==j){
                //     dp[i][j] += 1;
                // }
            }
    }