最长上升子序列

最长上升的O(n^2)的方法是我看过最简单的DP了~~

拿POJ 2533来说。

Sample Input

7
1 7 3 5 9 4 8

Sample Output(最长上升/非降子序列的长度)

4

从输入的序列中找出最长的上升子序列(LIS)。

d(i)=max(1,d(j)+1)其中(a[i]>a[j])其中这里的i,j均为下标。

含义是讨论到第i个数的最长上升子序列,要么是1,即都比前面的小,否则一定是由前面某一个dp(j)其中(a[j]<a[i])加1得到.

当然最后得到的总体的最长上升序列的长度为dp数组中的最大值,注意不是最后一个比如2,3,5,1前面3个数,每一个数的dp都是由前面的dp中最大+1继承而来,而最后一个比前面每一个数都小。

#include <stdio.h>
#include <string.h>
int dp[10001];
int a[1001];
 int main()
 {
     int n,m,i,j,max;
     while(scanf("%d",&n)==1)
     {
     memset(a,0,sizeof(a));
     memset(dp,0,sizeof(dp));
     for (i=1;i<=n;i++)
        scanf("%d",&a[i]);
        max=0;
    for (i=1;i<=n;i++)
     {
         dp[i]=1;
         for (j=1;j<=i-1;j++)
            if ((a[j]<a[i])&&(dp[i]<dp[j]+1))//每一次只需要加那个前面dp(j)的最高一次,而如果加了就是dp(i)=dp(j)+1即如果dp(i)如果小于dp(j)+1就要更新,否则肯定保持当前较大的dp(i)
         {
             dp[i]=dp[j]+1;
         }
         if (dp[i]>max)
            max=dp[i];
     }
     printf("%d\n",max);
     }
     return 0;

 }

这个解法不是我想的,从网上学来的,这里面与大家分享一下。这个算法的复杂度只有O(nlogn),在有大量数据的情况下,这算法效率极高。。。

(摘录原作者的话)

这个算法其实已经不是DP了,有点像贪心。至于复杂度降低其实是因为这个算法里面用到了二分搜索。本来有N个数要处理是O(n),每次计算要查找N次还是O(n),一共就是O(n^2);现在搜索换成了O(logn)的二分搜索,总的复杂度就变为O(nlogn)了。

这个算法的具体操作如下(by RyanWang):

开一个栈,每次取栈顶元素top和读到的元素temp做比较,如果temp > top 则将temp入栈;如果temp < top则二分查找栈中的比temp大的第1个数,并用temp替换它。 最长序列长度即为栈的大小top。

这也是很好理解的,对于x和y,如果x < y且Stack[y] < Stack[x],用Stack[x]替换Stack[y],此时的最长序列长度没有改变但序列Q的''潜力''增大了。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8; 再读6,用6替换8,得到1,3,6;再读7,得到最终栈为1,3,6,7。最长递增子序列为长度4。

#include <stdio.h>
int s[1001];
 int main()
 {

     int i,n,top,temp;
     while(scanf("%d",&n)==1)
     {
     top=0;
     s[top]=-1;//保证最小
     for (i=1;i<=n;i++)
     {
         scanf("%d",&temp);
         if (temp>s[top])//如果比栈顶元素大则入栈成为栈顶元素
            s[++top]=temp;
         else//否则二分查找比temp大的第一个数
         {
             int low=1;
             int high=top;
             int middle;
             while(low<=high)
             {
                 middle=(low+high)/2;
                 if (s[middle]<temp)
                    low=middle+1;
                 else
                    high=middle-1;
             }
             s[low]=temp;//用temp代替比temp大的第一个数代替,总数不变
         }
     }
     printf("%d\n",top);
     }
     return 0;
 }





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值