简体   繁体   English

最长递增子序列错误答案

[英]longest increasing subsequence wrong answer

I wrote a recursive solution for the longest increasing subsequence and it worked perfectly fine.我为最长的递增子序列编写了一个递归解决方案,它工作得非常好。 But when I applied dp on the same code it gives different answers.但是当我在相同的代码上应用 dp 时,它给出了不同的答案。 Problem Link: https://practice.geeksforgeeks.org/problems/longest-increasing-subsequence-1587115620/1 Recursive code:问题链接: 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 Code: 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;
}

I cannot figure out what I did wrong?我无法弄清楚我做错了什么? For this test case 6 (n) 6 3 7 4 6 9 (arr[]) Recursive code gives 4 answer(correct) But DP code gives 3 answer(incorrect)对于这个测试用例 6 (n) 6 3 7 4 6 9 (arr[]) 递归代码给出 4 个答案(正确)但 DP 代码给出 3 个答案(不正确)

When I think of dynamic programming, I usually break it down into two steps:当我想到动态编程时,我通常将其分解为两个步骤:

  1. Solve the recursion with "including the current element before recursing again" compared to "not including the current element before recursing again".与“在再次递归之前不包括当前元素”相比,使用“在再次递归之前包含当前元素”解决递归。 This is exactly what you did with your recursive solution.这正是您对递归解决方案所做的。

  2. Take the recursive solution from step 1 and add a cache of previous computed results to avoid repetitive recursion.从步骤 1 中获取递归解决方案并添加先前计算结果的缓存以避免重复递归。 The cache, can be conceptualized as a multidimension matrix that maps all the non-const variable parameters passed to the recursive function to the final result.缓存可以概念化为一个多维矩阵,它将传递给递归 function 的所有非常量变量参数映射到最终结果。

In your case, each recursive step has two variables, currIndex , and maxVal .在您的情况下,每个递归步骤都有两个变量currIndexmaxVal a and n are actually constants throughout the entire recursion. an实际上是整个递归过程中的常数。 The number of non-const parameters of the recursive step is the number of dimensions in your cache .递归步骤的非常量参数的数量是缓存中的维数 So you need a two dimensional table.所以你需要一个二维表。 We could use a big 2-d int array, but that would take a lot of memory.我们可以使用一个大的二维 int 数组,但这需要很多 memory。 We can achieve the same efficiency with a nested pair of hash tables.我们可以使用一对嵌套的 hash 表实现相同的效率。

Your primary mistake is that your cache is only 1 dimension - caching the result compared to currIndex irrespective of the value of maxVal .您的主要错误是您的缓存只有一维 - 与 currIndex 相比缓存结果,而与currIndex的值maxVal The other mistake is using a vector instead of a hash table.另一个错误是使用向量而不是 hash 表。 The vector technique you have works, but doesn't scale.您拥有的矢量技术有效,但无法扩展。 And when we add a second dimension, the scale in terms of memory use are even worse.而当我们添加第二个维度时,memory 的使用规模就更差了。

So let's defined a cache type as an unordered_map (hash table) that maps currIndex to another hash table that maps maxVal to the result of the recursion.因此,让我们将缓存类型定义为 unordered_map(哈希表),它将currIndex映射到另一个 hash 表,该表将maxVal映射到递归结果。 You could also use tuples, but the geeksforgeeks coding site doesn't seem to like that.您也可以使用元组,但 geeksforgeeks 编码网站似乎不喜欢这样。 No bother, we can just define this:没关系,我们可以这样定义:

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

Then your DP solution is effectively just inserting a lookup into the CACHE at the top of the recursive function and an insertion into the CACHE at the bottom of the function.然后,您的 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;

}

Then the initial invocation with the input set to solve for is effectively passing INT_MIN as the intial maxVal and an empty cache:然后使用要求解的输入集的初始调用有效地将 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);

A minor optimization is to make a , n , and cache a member variable of the C++ class encapsulating your solution so that they don't have to be pushed onto the stack for each step of the recursion.一个小的优化是使ancache C++ class 的成员变量封装您的解决方案,这样就不必在递归的每个步骤中将它们推入堆栈。 The cache is getting passed by reference, so it's not that big of a deal.缓存是通过引用传递的,所以没什么大不了的。

You have 2 problems in your code:您的代码中有两个问题

Mistake 1错误 1

First in C++, the size of an array must be a compile-time constant .So, take for example the following code snippets:首先在 C++ 中,数组的大小必须是编译时常量。因此,以以下代码片段为例:

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

The correct way to write the above would be:写上面的正确方法是:

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

Similarly, the following(which you did in your code example) is incorrect:同样,以下(您在代码示例中所做的)不正确:

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

Mistake 2错误 2

Second in your function LISdp , it seems to me that there is no need of the statement第二在您的 function LISdp中,在我看来,不需要声明

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

You should just remove this(the above) statement and the program produces expected output 4 as can be seen here .您应该只删除这个(上面的)语句,程序会产生预期的 output 4 ,如下所示 Basically you have not thought this(LISdp's working) through.基本上你还没有想到这一点(LISdp 的工作)。 You can use the debugger to see where you're going wrong.您可以使用调试器查看哪里出错了。

There might be other problems in your code but so far i am able to spot these 2.您的代码中可能还有其他问题,但到目前为止我能够发现这两个问题。

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

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