繁体   English   中英

最长递增子序列错误答案

[英]longest increasing subsequence wrong answer

我为最长的递增子序列编写了一个递归解决方案,它工作得非常好。 但是当我在相同的代码上应用 dp 时,它给出了不同的答案。 问题链接: https://practice.geeksforgeeks.org/problems/longest-increasing-subsequence-1587115620/1递归代码:

int LISrecursive(int arr[], int n, int currIndex, int maxVal) {
    if (currIndex == n) {
        return 0;
    }
    int included = 0, notIncluded = 0;
    if (arr[currIndex] > maxVal) {
        included = 1 + LISrecursive(arr, n, currIndex + 1, arr[currIndex]);
    }
    notIncluded = LISrecursive(arr, n, currIndex + 1, maxVal);

    return max(notIncluded, included);

}

DP 代码:

int LISdp(int arr[], int n, int currIndex, int maxVal, vector<int> &dp) {
    if (currIndex == n) {
        return 0;
    }
    if (dp[currIndex] != -1) return dp[currIndex];
    int included = 0, notIncluded = 0;
    if (arr[currIndex] > maxVal) {
        included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], dp);
    }
    notIncluded = LISdp(arr, n, currIndex + 1, maxVal, dp);

    return dp[currIndex] = max(notIncluded, included);

}
int32_t main() {
    int n;
    cin >> n;
    int arr[n];
    vector<int> dp(n, -1);
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    cout << LISrecursive(arr,n,0,-1); 
    cout << LISdp(arr, n, 0 , -1, dp);
    return 0;
}

我无法弄清楚我做错了什么? 对于这个测试用例 6 (n) 6 3 7 4 6 9 (arr[]) 递归代码给出 4 个答案(正确)但 DP 代码给出 3 个答案(不正确)

当我想到动态编程时,我通常将其分解为两个步骤:

  1. 与“在再次递归之前不包括当前元素”相比,使用“在再次递归之前包含当前元素”解决递归。 这正是您对递归解决方案所做的。

  2. 从步骤 1 中获取递归解决方案并添加先前计算结果的缓存以避免重复递归。 缓存可以概念化为一个多维矩阵,它将传递给递归 function 的所有非常量变量参数映射到最终结果。

在您的情况下,每个递归步骤都有两个变量currIndexmaxVal an实际上是整个递归过程中的常数。 递归步骤的非常量参数的数量是缓存中的维数 所以你需要一个二维表。 我们可以使用一个大的二维 int 数组,但这需要很多 memory。 我们可以使用一对嵌套的 hash 表实现相同的效率。

您的主要错误是您的缓存只有一维 - 与 currIndex 相比缓存结果,而与currIndex的值maxVal 另一个错误是使用向量而不是 hash 表。 您拥有的矢量技术有效,但无法扩展。 而当我们添加第二个维度时,memory 的使用规模就更差了。

因此,让我们将缓存类型定义为 unordered_map(哈希表),它将currIndex映射到另一个 hash 表,该表将maxVal映射到递归结果。 您也可以使用元组,但 geeksforgeeks 编码网站似乎不喜欢这样。 没关系,我们可以这样定义:

typedef std::unordered_map<int, std::unordered_map<int, int>> CACHE;

然后,您的 DP 解决方案实际上只是将查找插入到递归 function 顶部的缓存中,并插入到 function 底部的缓存中。

int LISdp(int arr[], int n, int currIndex, int maxVal, CACHE& cache) {
    if (currIndex == n) {
        return 0;
    }

    // check our cache if we've already solved for currIndex and maxVal together
    auto itor1 = cache.find(currIndex);
    if (itor1 != cache.end())
    {
        // itor1->second is a reference to cache[currIndex]
        auto itor2 = itor1->second.find(maxVal);
        if (itor2 != itor1->second.end())
        {
            // itor2->second is a reference to cache[n][maxVal];
            return itor2->second;
        }
    }

    int included = 0, notIncluded = 0;
    if (arr[currIndex] > maxVal) {
        included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], cache);
    }
    notIncluded = LISdp(arr, n, currIndex + 1, maxVal, cache);

    // cache the final result into the 2-d map before returning
    int finalresult = std::max(notIncluded, included);
    cache[currIndex][maxVal] = finalresult; // cache the result
    return finalresult;

}

然后使用要求解的输入集的初始调用有效地将 INT_MIN 作为初始maxVal和一个空缓存传递:

int N = 16
int A[N]={0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};

CACHE cache;
int result = LISdp(A, N, 0, INT_MIN, cache);

一个小的优化是使ancache C++ class 的成员变量封装您的解决方案,这样就不必在递归的每个步骤中将它们推入堆栈。 缓存是通过引用传递的,所以没什么大不了的。

您的代码中有两个问题

错误 1

首先在 C++ 中,数组的大小必须是编译时常量。因此,以以下代码片段为例:

int n = 10;
int arr[n]; //INCORRECT because n is not a constant expression

写上面的正确方法是:

const int n = 10;
int arr[n]; //CORRECT

同样,以下(您在代码示例中所做的)不正确:

 int n;
 cin >> n;
 int arr[n];// INCORRECT because n is not a constant expression

错误 2

第二在您的 function LISdp中,在我看来,不需要声明

if (dp[currIndex] != -1) return dp[currIndex];//no need for this statement

您应该只删除这个(上面的)语句,程序会产生预期的 output 4 ,如下所示 基本上你还没有想到这一点(LISdp 的工作)。 您可以使用调试器查看哪里出错了。

您的代码中可能还有其他问题,但到目前为止我能够发现这两个问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM