【问题标题】:Longest subsequence that first increases then decreases先增后减的最长子序列
【发布时间】:2012-03-19 03:07:39
【问题描述】:

我正在尝试解决以下问题:


元素值先减小后增大的序列称为V-序列。在有效的 V-Sequence 中,递减臂中至少应有一个元素,递增臂中应至少有一个元素。

例如,“5 3 1 9 17 23”是一个有效的 V-Sequence,在递减臂中有两个元素,即 5 和 3,在递增臂中有 3 个元素,即 9、17 和 23。但是序列“6 4 2”或“8 10 15”都不是V-Sequence,因为“6 4 2”在增加部分没有元素,而“8 10 15”在减少部分没有元素。

通过从序列中删除零个或多个元素获得序列的子序列。例如定义“7”、“2 10”、“8 2 7 6”、“8 2 7 10 6”等是“8 2 7 10 6”的有效子序列

给定一个包含 N 个数字的序列,找到它的最长子序列,即 V-Sequence。


我目前有一个 O(n^2) 解决方案,其中我首先初始化一个数组 (m[]),使得每个 m[i] 包含从数组中的“i”开始的最长递增序列。

同样,我初始化了另一个数组( d[] ),这样每个 d[i] 都包含在该点上的最长递减序列 ENDING。

这两个操作都需要 O( n^2 )

我现在遍历这些数组并选择 m[i] + d[i] -1 的最大值,从而满足所需的条件。

我想知道的是 - 是否有 O(n lg n) 解决方案?因为我的解决方案没有在要求的时间限制内运行。谢谢你:)

代码:

#include<cstdio>
#include<algorithm>

using namespace std;



int m[ 200000 ];
int d[200000 ];
int n;
int arr[200000 ];

void LIS()
{
    m[ n-1 ] = 1;

    int maxvPos = -1;
    int maxv = -1;

    for( int i=n-2; i>=0; i-- )
    {
        maxv = -1;
        for( int j=i+1; j<n; j++ )
        {
            if( ( m[j]+1 > maxv ) && ( arr[i] < arr[j]) )
            {
                maxv = m[j]+1;
                maxvPos = j;
            }


        }
        if( maxv>0 )
            {
                m[i] = maxv;
            }

            else
                m[i ] = 1;
    }

 }

void LDS()
{
      d[0] = 1;

    int maxv = -1;
    int maxvPos = -1;

    for( int i=1; i<n; i++ )
    {
        maxv = -1;
        for( int j=i-1; j>=0; j-- )
        {
            if( ( d[j]+1 > maxv) && arr[j]>arr[i] )
            {
                maxv = d[j]+1;
                maxvPos = j;
            }
        }

        if( maxv>0 )
            d[i] = maxv;

        else
            d[i]=1;
    }

}

int solve()
{
    LIS();
    LDS();

    int maxv = 0;
    int curr = 0;

    for( int i=0; i<n; i++ )
    {
        curr = d[i] + m[i] -1 ;

        if( ( d[i]>0) && (m[i]>0 ))
        {
            if( curr != 1 )
            maxv = max( curr, maxv );
        }

    }

    return maxv;

}

/*    static void printArr( int[] a )
{
    for( int i : a )
        System.out.print( i + " ");

    System.out.println();
} */


int main()
{
    scanf( "%d", &n );

    for( int i=0; i<n; i++ )
    {
        scanf("%d", &arr[i] );
    }   

    printf("%d\n", solve() );
    return 0;

}

【问题讨论】:

  • 它来自昨晚举行的一次编程比赛。我的提交超过了 11 个测试用例中的 6 个的时间限制。

标签: algorithm dynamic-programming


【解决方案1】:

最长递增子序列问题有O(NlgK)算法,其中K是LIS长度。您可以查看Wikipedia 以获取该算法的描述。 LightOJ 也有一个很好的教程(这可能需要登录)。

【讨论】:

  • 谢谢 :) 维基百科链接没有多大帮助,但第二个链接非常好!
【解决方案2】:

编辑:哦,这个答案是错误的。我错过了关于能够删除元素以制作更长的符合序列的部分。不过,为了娱乐,这里有一个解决方案,可以解决您无法删除元素的简单情况:

我能想到一个 O(n) 的解决方案:

遍历列表一次。维护一些变量:

  • 看到的最长 v 序列的开始
  • 看到的最长 v 序列的长度
  • 当前 v-sequence 的开始
  • 当前扫描位置
  • 当前扫描状态(升序或降序)

初始化指向第一个元素的起始指针,最长为 0,扫描状态为降序。

  1. 只要数字在下降并且处于下降状态,就遍历列表。
  2. 当遇到越来越多的时候,切换到上升状态,继续走
  3. 当遇到下一个递减数字时,这就是 v 序列的结尾。
  4. 与当前遇到的最长 v 序列进行比较,如果这个更长,则更新。
  5. 将当前垂直序列的开始和扫描状态重置为降序
  6. 走下一个序列
  7. 在数组末尾,返回最长序列的起点和长度。

【讨论】:

  • 答案需要一个不一定连续的子序列。
  • 我认为你误解了这个问题。子序列不必是连续的。例如,对于输入“1 4 2 7 3”,结果应该是 4 [1 4 7 3]。
【解决方案3】:

这是一个 O(n) 解决方案。 检查了基本示例。
如果它有任何问题或不适用于任何特定情况,请告诉我。

代码:

#include<stdio.h>
int max(int a,int b)
{
return (a >= b ? a : b);
}
int main()
{
    int i,j,n;
    scanf("%d",&n);
    int A[200022];
    int dec[200022]={0};
    int V[200022]={0};
    int state[200022]={0};
    for(i=0;i<n;i++)
    {
      scanf("%d",&A[i]);
    }
    if(A[0] > A[1])
        state[0]=1;
    for(i=1;i<n;i++)
    {
        j=i-1;
            if(A[i] < A[j])
            {    
                dec[i]=max(dec[i],dec[j]+1);
                V[i]=max(V[i],V[j]);
                state[i]=1;
            }    
            else if(A[i] == A[j])
            {    
                dec[i]=dec[j];
                V[i]=V[j];
                state[i]=state[j];
            }
            else
            {
                if(state[j]==1)
                {
                    dec[i]=dec[i];
                    V[i]=max(V[i],dec[j]+1);
                    V[i]=max(V[i],V[j]+1);
                    state[i]=1;
                }
                else
                {
                    dec[i]=dec[i];
                    V[i]=max(V[i],V[j]);
                }
            }

//  printf("%d %d\n",dec[i],V[i]);
}
    if(V[n-1] == 0)
        printf("0\n");
    else
        printf("%d\n",V[n-1]+1);
 }

【讨论】:

    【解决方案4】:

    构造数组inc[i],其中inc[i]存储以A[i]结尾的最长递增子序列。 构造数组 dec[i],其中 dec[i] 存储以 A[i] 结尾的最长递减子序列。

    然后求(inc[i] + dec[i] - 1)的最大值

    【讨论】:

      猜你喜欢
      • 2016-04-11
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多