是否存在最长递增子序列的自顶向下动态规划解决方案?

问题描述 投票:0回答:5
我想知道如何使用自顶向下动态规划查找数组的 LIS。 是否存在这样一种解决方案?你能给我使用自顶向下动态规划查找数组 LIS 的伪代码吗?我无法在互联网上找到一个。他们都使用自下而上。

algorithm dynamic-programming pseudocode
5个回答
11
投票
在 Java 中解决 LIS 长度的递归方法如下 -

public int LIS(int[] arr) { return LISLength(arr, Integer.MIN_VALUE, 0); } public int LISLength(int[] arr, int prev, int current) { if (current == arr.length) { return 0; } int include = 0; if (arr[current] > prev) { include = 1 + LISLength(arr, arr[current], current + 1); } int exclude = LISLength(arr, prev, current + 1); return Math.max(include, exclude); }

但是它的时间复杂度为 O(2^n),因此我们需要使用记忆技术来通过以下方法降低复杂性 -

public int LIS(int[] arr) { int memoTable[][] = new int[arr.length + 1][arr.length]; for (int[] l : memoTable) { Arrays.fill(l, -1); } return LISLength(arr, -1, 0, memoTable); } public int LISLength(int[] arr, int prev, int current, int[][] memoTable) { if (current == arr.length) { return 0; } if (memoTable[prev + 1][current] >= 0) { return memoTable[prev + 1][current]; } int include = 0; if (prev < 0 || arr[current] > arr[prev]) { include = 1 + LISLength(arr, current, current + 1, memoTable); } int exclude = LISLength(arr, prev, current + 1, memoTable); memoTable[prev + 1][current] = Math.max(include, exclude); return memoTable[prev + 1][current]; }

因此 O(n^2) 将通过记忆技术优化时间复杂度。


4
投票
当然。定义:

F(n) = 序列 1..n 的最长递增子序列 ,并且序列

必须以元素 n 结尾

然后我们得到递归函数(自上而下):

F(n) = max(len(F(i)) + 1) 其中 0

<= i < n and array[i] < array[n]

所以答案是:

F(1..n) 的最长递增子序列

通过

memoization,我们得到这段代码(这是Python,它比伪代码更好):

d = {} array = [1, 5, 2, 3, 4, 7, 2] def lis(n): if d.get(n) is not None: return d[n] length = 1 ret = [array[n]] for i in range(n): if array[n] > array[i] and len(lis(i)) + 1 > length: length = len(lis(i)) + 1 ret = lis(i) + [array[n]] d[n] = ret return ret def get_ans(): max_length = 0 ans = [] for i in range(len(array)): if max_length < len(lis(i)): ans = lis(i) max_length = len(lis(i)) return ans print get_ans() # [1, 2, 3, 4, 7]
    

1
投票
我总是尝试编写与自上而下和自下而上相同的逻辑。 我的自下而上的进展:

#include "bits/stdc++.h" using namespace std; typedef long long ll; int n; vector<int> a, dp; int main() { ios_base::sync_with_stdio(0); cin.tie(); cin >> n; a.resize(n); dp.resize(n); for (auto &x : a) { cin >> x; } for (int i = 0; i < n; i++) { dp[i] = 1; for (int j = 0; j < i; j++) { if (a[j] < a[i]) { dp[i] = max(dp[i], 1 + dp[j]); } } } int ans = *max_element(dp.begin(), dp.end()); cout << ans << '\n'; }
然后我将此解决方案转换为自顶向下:

#include "bits/stdc++.h" using namespace std; typedef long long ll; int n; vector<int> a, dp; int calc(int i) { if (dp[i] != -1) { return dp[i]; } dp[i] = 1; for (int j = i - 1; j >= 0; j--) { if (a[j] < a[i]) { dp[i] = max(dp[i], 1 + calc(j)); } } return dp[i]; } int main() { ios_base::sync_with_stdio(0); cin.tie(); cin >> n; a.resize(n); dp.resize(n, -1); for (auto &x : a) { cin >> x; } int ans = 0; for (int i = n - 1; i >= 0; i--) { ans = max(ans, calc(i)); } cout << ans << '\n'; }
    

0
投票
是的,存在一个自上而下的递归记忆版本。

数组中的每个元素,如果大于前一个元素,则可以选择是否成为递增子序列的一部分。

如果它不是递增子序列的一部分,我们忽略该元素并且不将其添加到子序列的长度中。

最后我们需要返回我们所做的两个选择中的最大值。

您可以在下面找到Python代码。

class Solution: def longestSubsequence(self,a,n): ''' Every element, if larger than the prev ele, has a choice, be a part of the increasing subseq or not be ''' memo = {} def solve(i,prev): if i>=n: return 0 ky = (i,prev) if ky in memo: return memo[ky] ways1 = 0 if prev==None: ways1 = 1+solve(i+1,a[i]) elif a[i]>prev: ways1 = 1 + solve(i+1,a[i]) ways2 = solve(i+1,prev) memo[ky] = max(ways1,ways2) return memo[ky] return solve(0,None)
这里字典将用作记忆数据结构。关键是对 

因为两者都可能在每次迭代中发生变化。

我们可以使用此备忘录来检索重叠的子问题。


-2
投票
自上而下的方法

#include <iostream> using namespace std; int main() { int t;cin>>t; while(t--){ int n; cin>>n; int arr[n]; for(int i=0;i<n;i++) cin>>arr[i]; int lis[n]; for(int i=0;i<n;i++) lis[i]=1; lis[0]=1; for(int i=0;i<n;i++) { for(int j=i+1;j<n;j++) { if(arr[i]<arr[j] && lis[i]+1>lis[j]) lis[j] = lis[i]+1; } } int ans = 1; for(int i=0;i<n;i++) ans = max(ans,lis[i]); cout<<ans<<endl; } return 0; }
    
© www.soinside.com 2019 - 2024. All rights reserved.