2 条题解

  • 13
    @ 2023-7-6 16:38:43

    本题中的峡谷序列,原理和山峰序列类似,但需要注意区分,否则容易陷入误区。 可能会有同学尝试在山峰序列代码的基础上交换f数组和g数组而陷入误区,这是因为对f数组和g数组的状态定义理解的不够清楚,我们在山峰子序列问题中用f[i]表示从左往右以i结尾时的最长上升子序列长度,g[i]表示从右往左以i结尾时的最长上升子序列长度,若想要表示以i结尾时的最长下降子序列长度,则需要调整中间的查询过程。

    本题的数据规模较小,可以使用O(n²)时间复杂度算法。

    参考代码
    for (int i = 1; i <= n; i++)  
        {  
        	f[i] = 1;
            for (int j = 1; j < i; j++)
    		{
            	if (a[j] > a[i])
            	   f[i] = max(f[i], f[j] + 1); 
    		}
        }
        for (int i = n; i >= 1; i--)  
        {  
        	g[i] = 1;
            for (int j = n; j > i; j --)
            {
            	if (a[j] > a[i])
            	   g[i] = max(g[i], g[j] + 1);
    		} 	
        }
    

    如果使用二分优化,则需要将d数组初始为0,并且在二分查找时查询d数组中第一个小于等于a[i]的数的下标。

    参考代码
    int max_len(int x)
    {
        int l = 1, r = n, ans = n + 1;
        while (l <= r)
        {
            int mid = (l + r) / 2;
            if (d[mid] <= x)
            {
                ans = mid;
                r = mid - 1;
            }
            else
            {
                l = mid + 1;
            }
        }
        return ans;
    }
    
    for (int i = 1; i <= n; i++)
        {
            int now = max_len(a[i]);
            f[i] = now;
            d[now] = a[i];
        }
        memset(d, 0, sizeof(d));
        for (int i = n; i >= 1; i--)
        {
            int now = max_len(a[i]);
            g[i] = now;
            d[now] = a[i];
        }
    
    • 9
      @ 2023-8-1 1:40:00

      这道题目要求找出给定序列中的最长峡谷子序列的长度。峡谷子序列是指先下降再上升的子序列。我们可以使用动态规划来解决这个问题。

      首先,我们定义两个数组f和g,分别用来表示从左往右和从右往左的最长峡谷子序列长度。

      对于f数组,我们从左往右遍历序列a,对于每个元素a[i],我们在其之前的所有元素中查找比它大的元素,并更新f[i]为其之前的最大峡谷子序列长度加1。即 f[i] = max(f[j] + 1, f[i]),其中j取值范围为[1, i-1],且a[j] > a[i]。

      对于g数组,我们从右往左遍历序列a,对于每个元素a[i],我们在其之后的所有元素中查找比它大的元素,并更新g[i]为其之后的最大峡谷子序列长度加1。即 g[i] = max(g[j] + 1, g[i]),其中j取值范围为[i+1, n],且a[j] > a[i]。

      最后,我们遍历f和g数组,计算f[i] + g[i] - 1的最大值,即为最长峡谷子序列的长度。

      在实现过程中,我们可以使用二分查找来优化查找比当前元素大的最远位置的过程。具体做法是,维护一个辅助数组d,其中d[i]表示长度为i的最长上升子序列的末尾元素的最小值。在二分查找时,我们查询d数组中第一个小于等于当前元素a[i]的位置,即为满足条件的最远位置。

      以上就是解决这个问题的思路和步骤。通过动态规划和二分查找,我们可以高效地求解最长峡谷子序列的长度。

      #include <iostream>
      #include <vector>
      #include <cstring>
      using namespace std;
      
      int max_len(int x, vector<int>& d) {
          int l = 1, r = d.size() - 1, ans = d.size();
          while (l <= r) {
              int mid = (l + r) / 2;
              if (d[mid] <= x) {
                  ans = mid;
                  r = mid - 1;
              } else {
                  l = mid + 1;
              }
          }
          return ans;
      }
      
      int longestValleySubsequence(int n, vector<int>& a) {
          vector<int> f(n + 1, 1);
          vector<int> g(n + 1, 1);
          vector<int> d(n + 1, 0);
          
          for (int i = 1; i <= n; i++) {
              int now = max_len(a[i], d);
              f[i] = now;
              d[now] = a[i];
          }
          
          memset(d.data(), 0, sizeof(int) * (n + 1));
          
          for (int i = n; i >= 1; i--) {
              int now = max_len(a[i], d);
              g[i] = now;
              d[now] = a[i];
          }
          
          int maxLength = 0;
          
          for (int i = 1; i <= n; i++) {
              maxLength = max(maxLength, f[i] + g[i] - 1);
          }
          
          return maxLength;
      }
      
      int main() {
          int n;
          cin >> n;
          vector<int> a(n + 1);
          for (int i = 1; i <= n; i++) {
              cin >> a[i];
          }
          int result = longestValleySubsequence(n, a);
          cout << result << endl;
          return 0;
      }
      
      • 1

      信息

      ID
      269
      时间
      1000ms
      内存
      256MiB
      难度
      4
      标签
      (无)
      递交数
      788
      已通过
      343
      上传者