如何将以下递归转换为自顶向下的动态编程?

时间:2019-02-08 18:08:35

标签: algorithm dynamic-programming

我正在尝试通过leetcode解决Maximum Product Subarray问题。

问题描述是:给定一个整数数组,在该数组中找到包含至少一个具有最大乘积的数字的连续子数组。

示例:输入:[2,3,-2,4],输出:6

为解决这个问题,我使用以下逻辑:让f(p,n)输出正确的结果,直到结果为p的数组的索引n。因此重复是:

f(p,n) = p // if(n=a.length)
f(p,n) = max( p, f(p*a[n], n+1), f(a[n], n+1) ) // otherwise

这适用于常规递归(下面的代码)。

private int f(int[] a, int p, int n) {
    if(n==a.length)
        return p;
    else
        return max(p, f(a, p*a[n], n+1), f(a, a[n], n+1));
}

但是,我无法将其转换为自上而下的动态编程。我一直使用的将递归程序转换为使用自上而下的DP的方法是:

  • 初始化缓存(我将使用数组)
  • 如果索引“ n”处的缓存已满,则返回该值作为结果
  • 否则递归并将结果存储在缓存中
    • 从缓存中返回值。

这是我一直使用的一种通用方法,它已经解决了我已经完成的大多数dp问题,但是不适用于此问题。

使用此方法的(不正确)代码如下所示:

private int f(int[] a, int p, int n, int[] dp) {
    if(dp[n]!=0)
        return dp[n];
    if(n==a.length)
        dp[n] = p;
    else
        dp[n] = max(p, f_m(a, p*a[n], n+1, dp), f_m(a, a[n], n+1, dp));
    return dp[n];
}

我从主函数调用函数如下:

// int x = f(a, a[0], 1, dp); - for incorrect top-down dp attempt
// int x = f(a, a[0], 1); - for regular recursion

不起作用的示例是:[3,-1,4]。在这里,它错误地输出3而不是4。

据我了解,问题在于这两个子问题都引用了DP数组的相同n + 1索引,因此仅解决了1个子问题,这导致了错误的答案。

所以我的问题是:

如何将这种重复性转换为自上而下的DP程序?对于这种情况,我可以遵循一种通用方法吗?

2 个答案:

答案 0 :(得分:0)

您的dp状态取决于当前索引n和当前结果p。因此,您需要在2D数组中记住结果,而不是仅对索引使用1D数组。

private int f(int[] a, int p, int n, int[] dp) {
    if(dp[n][p]!=0)
        return dp[n][p];
    if(n==a.length)
        dp[n][p] = p;
    else
        dp[n][p] = max(p, f_m(a, p*a[n], n+1, dp), f_m(a, a[n], n+1, dp));
    return dp[n][p];
}

答案 1 :(得分:0)

您可以按照尝试的方式进行操作,但是,我将为您建议一种解决其o(n)问题的简便方法,甚至不需要存储数组,因此不需要o(1)空间。

让我们保留2个变量的最小值和最大值。它存储了到目前为止的最小和最大乘积,由于-ve数,我们保留min标记,因为两个负数可以乘积为大数。

休息很容易

初始化min = 1和max = 1且ans = 0(因为qs说至少需要一个数字,所以您可以相应地更改此初始化),即第一个元素。

一次开始读取输入的一个元素,说“ a” 遍历数组的长度

{

if(a> 0)

max = a * max;
min =(1

否则为(a <0)

max =(1> min * a)? 1:分钟* a; min = max * a;

其他

max = 1; min = 1;

ans =(ans> max)吗? ans:最大; //这在其他地方

}

最大循环数结尾处将是答案,很高兴编码:)