最长上升子序列

求一个数组中最长上升子序列长度

n^2做法:

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
using namespace std;
const int INF = 100000;

int a[INF], dp[INF]; // a数组为数据,dp[i]表示以a[i]结尾的最长递增子序列长度

//求完dp数组后,取其中的最大值就是最大上升子序列的长度
/*
状态设计:dp[i]代表以a[i]结尾的LIS的长度
状态转移:dp[i]=max(dp[i], dp[j]+1) (0<=j< i, a[j]< a[i])
c初始状态:dp[i]=1 ,即每一个单独数字上升序列就是自己


开一个和原数组长度一样的数组 dp ,dp[i] 表示从a[0]开始,以a[i]为结尾的最长上升子序列
思路是对于每一个a[i]找出它的最长上升子序列供后面的操作使用,即代码段
for (int j = 0; j < i; j++) {
   if (a[i] > a[j]) {
     dp[i] = max(dp[i], dp[j] + 1);
   }
}
做的事。

*/

int main()
{
    int n;
    while (sfd(n), n) {
        for (int i = 0; i < n; i++)
            sfd(a[i]),dp[i]=1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (a[i] > a[j]) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }
        }

        int ans = 0;
        for (int i = 0; i < n; i++)
            ans = max(ans, dp[i]);
        pfd(ans);
        system("pause");
    }


    return 0;
}

nlog(n)做法

贪心+二分

在dp数组中保存最长上升子序列(实际上并不是真正的最长子序列,只是长度和最长子序列一样而已),只要维护好dp数组的结构就好了
举个例子:序列 1 3 7 5 6 4 9 11 15
从a[0]开始到a[n-1]对于每一个数,尝试将放入dp数组合适位置
首先 a[0]放入 dp[0]=a[0]; dp={1}
对于a[1],因为a[1]>dp[0],所以a[1]放入;dp={1,3}
对于a[2],因为a[2]>dp[1],所以a[2]放入;dp={1,3,7}

对于a[3],因为a[3]
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef  long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define sf2d(x,y) scanf("%d %d",&(x),&(y))
#define sfd(x) scanf("%d",&x)
#define sff(p) scanf("%lf",&p)
#define pfd(x) printf("%d\n",x)
#define mset(x,b) memset((x),b,sizeof(x))
const double pi = acos(-1.0);
using namespace std;
const int INF = 100000;

int a[INF], dp[INF]; 


int main()
{
    int n;
    while (sfd(n), n) {
        for (int i = 0; i < n; i++)
            sfd(a[i]),dp[i]=1;
        mset(dp, -1);
        int len = 0;
        dp[len] = a[0];
        for (int i = 1; i < n; i++){
            if (dp[len] < a[i])
                dp[++len] = a[i];//大于,直接接在尾部
            else {//不然就二分查找合适位置
                int left = 0;
                int right = len;
                int mid;
                bool mark = false;
                while (left <= right) {
                    mid = left + (right - left) / 2;
                    if (a[i] > dp[mid] && a[i] < dp[mid + 1]) {
                        dp[mid + 1] = a[i];

                        mark = true;
                        break;
                    }

                    else if (dp[mid+1] < a[i])
                        left = mid;
                    else if (dp[mid] > a[i])
                        right = mid;

                }

                if(!mark)
                    dp[mid + 1] = a[i];
            }
        }

        pfd(len+1);//dp是从0开始,并且是先用后加,所以要输出len+1

        system("pause");
    }


    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值