[acm] 动态规划——最长上升子序列

最长上升子序列

子序列简述

对于数列E = { 3 1 2 1 8 5 6 },{ 1 2 1}是E的子序列,{ 3 2 8 6}也是E的子序列

E最长上升子序列为 { 1 2 5 6 };

1

用dp的思维来解决问题(O( n^2 ))

dp[i] 存的是在这个数之前的最长字序列(或者最长子序列和,etc),用当前状态与更新状态比较

 dp[i] = max(dp[j] + 1,dp[i]);//这个 + 1就相当于把直接算进去与之前的值比较(可以手动输出下看看差别)

最后的模板

int a[1005];
int dp[1005];
int main()
{
    int n;
    cin >> n;
    for(int i =0 ; i < n; i++)    cin >> a[i];///输入
    
    int ans = 0;
    dp[0] = 1;//初始化,后面就直接冲i=1开始枚举
    
    for(int i = 1; i < n ; i++){
        for(int j = 0; j < i; j ++){
            if(a[i] > a[j]){
                dp[i] = max(dp[j] + 1,dp[i]);///之前的状态加上自己(dp[j] + 1)
                                             ///与上次枚举的状态比较(dp[i])
            }
        }
        
        dp[i] = max(1,dp[i]);//因为j没有枚举i位,所有对于一个数的时候,更新1
        ans = max (ans,dp[i]);
    }

    cout << ans << endl;
}

二分求解( O( nlogn ) )

这个方法只能求得子序列的长度,无法求和( 因为最后求得的序列不是有效解,但是数目是统一的 )

对于序列 D = { 3 1 2 5 6 1 2 4 1 8 9 10 11 }
最后存下来的序列为 { 1 2 4 6 8 9 10 11 }
但是正确的应该是 { 1 2 5 6 8 9 10 11 }

核心思想

if( a[i] 大于 d[所记录的最后一个数] )d[数+1] = a[i]
else
替换掉第一个大于或等于他的 d[j] 所在位置的数 (用lower_bound查找)

最终代码

#include <bits/stdc++.h>

using namespace std;

int a[40005];
int d[40005];

int main()
{
    int n;
    cin >> n;
    for (int i=1 ; i<=n ; i++) cin >> a[i];
    
    if (n==0)  //0个元素特判一下 
    {
        printf("0\n");
        return 0;
    }
    
    d[1]=a[1];  ///初始化 
    int len=1;
    
    for (int i = 2 ; i <= n ; i++){
        
        //cout << a[i] <<" :" << endl; ///输出来看看如何运行的
        if (a[i] > d[len]) d[++len] = a[i];  ///如果可以接在len后面就接上
        else  ///否则就找一个最该替换的替换掉 
        {
            int j = lower_bound(d+1,d+len+1,a[i])-d;  ///找到第一个大于它的d的下标,
            //cout <<d[j] << " " << a[i] << endl; ///输出来看看如何运行的
	    d[j] = a[i]; 
        }
    }
    
  //  for(int i = 1; i <= n ; i++){
  //  	cout << d[i] << endl;
  //	} 
    
    cout << len << endl;    
    return 0;
}

/*
13
3 1 2 5 6 1 2 4 1 8 9 10 11
*/
posted @ 2020-12-09 00:17  Hoppz  阅读(116)  评论(0编辑  收藏  举报