最长上升子数列

来源:互联网 发布:淘宝商家怎么交保证金 编辑:程序博客网 时间:2024/06/05 11:18

  最长上升子数列

输入

第1行:1个数N,N为序列的长度(2 <= N <= 50000)第2 - N + 1行:每行1个数,对应序列的元素(-10^9 <= S[i] <= 10^9)

输出

输出最长递增子序列的长度。

输入示例

8516824510

输出示例

5
题解(转载):
   (LIS Longest Increasing Subsequence)给定一个数列,从中删掉任意若干项剩余的序列叫做它的一个子序列,求它的最长的子序列,满足子序列中的元素是单调递增的。

例如给定序列{1,6,3,5,4},答案是3,因为{1,3,4}和{1,3,5}就是长度最长的两个单增子序列。

处看此题,怎么做? 万能的枚举?枚举全部2^n个子序列,找出最长的,固然可以,就是复杂度太高。我们为什么要枚举呢?因为要知道取了哪些数,其实我们只需要考虑上一个数和取了几个数就可以了吧?因为单增的意思是比前一个数大,我们要加入这个数的时候,只考虑它比之前加入的最后一个数大就可以了。而最长的意思是数的个数最多,我们只要知道数的总个数就可以了,没必要知道具体有哪些数。

让我们尝试一下用动态规划的思考办法。首先设置数列是a1, a2, a3…an,为了方便我们加入一项a0=-∞,后面我们将发现这会给我们带来极大的方便。int f[i]表示以第i个数结尾的最长单调子序列的长度, 那么我们看一下加入ai之前的最后一个数是aj,显然j < i并且aj < ai,我们有f(i) = f(j) + 1,因为往后面延长了一项嘛。那根据这个式子,我们显然应该选择最大的f(j),才能让f(i)最大。

于是我们有了递推关系f(i) = max{f(j)| j < i并且aj < ai} + 1,光有了递推关系还不够,初值呢? f(0) = 0,并且我们加入了a0=-∞,这样对每个i > 0,j总是存在的,大不了就达到下标0了嘛。


代码:

//O(n^2)时间复杂度#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 50010;int dp[maxn], a[maxn];int main(){int n;while(scanf("%d",&n)!=EOF){for(int i=0;i<n;++i)              scanf("%d",&a[i]);          int ans=0;          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], dp[j]+1);              }               ans=max(dp[i],ans);          }          printf("%d\n",ans);  }return 0;} //O(nlogn)#include <cstdio>  #include <algorithm>  #define INF 0x3f3f3f  using namespace std;  int dp[50010],a[50010];  int main()  {      int n,i,j;      while(scanf("%d",&n)!=EOF)      {          for(i=0;i<n;++i)          {              scanf("%d",&a[i]);              dp[i]=INF;          }          for(i=0;i<n;++i)              *lower_bound(dp,dp+n,a[i])=a[i];  //二分思想        printf("%d\n",lower_bound(dp,dp+n,INF)-dp);       }      return 0;  }    



原创粉丝点击