zoukankan      html  css  js  c++  java
  • 最长递增(不减)子序列

    问题:

    拿POJ 2533来说。

    Sample Input

    7
    1 7 3 5 9 4 8

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

    4

    解法一(O(n^2)):

    如何把这个问题分解成子问题呢?经过分析,发现 “求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”是个好的子问题――这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。

    由上所述的子问题只和一个变量相关,就是数字的位置。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做为“终点”的最长上升子序列的长度。这个问题的状态一共有N个。状态定义出来后,转移方程就不难想了。假定MaxLen (k)表示以ak做为“终点”的最长上升子序列的长度,那么:

    MaxLen (1) = 1

    MaxLen (k) = Max { MaxLen (i):1<i < k 且 ai < ak且 k≠1 } + 1

    这个状态转移方程的意思就是,MaxLen(k)的值,就是在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。

    实际实现的时候,可以不必编写递归函数,因为从 MaxLen(1)就能推算出MaxLen(2),有了MaxLen(1)和MaxLen(2)就能推算出MaxLen(3)……

    解法二(O(nlog(n))):

    这个算法其实已经不是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 <iostream>
    #include <vector>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <assert.h>
    using namespace std;

    std::vector<int> v;
    std::vector<int> seq;
    int len;
    int record[10000];
    int before[10000];
    int stack[10000];
    int stackSize;
    bool bujian=false;
    void input(){
    cin >> len;
    for(int i = 0 ; i < len ; i++){
    int a;
    cin >> a;
    v.push_back(a);
    record[i] = 0;
    before[i] =-1;
    }
    stackSize = 0;
    }

    void output(){
    cout << len<<endl;
    for(int i = 0 ; i < v.size() ; i++){
    cout << v[i]<<" ";

    }
    }

    //O(n^2)
    int algorithm1(){
    record[0] = 1;

    for (int i = 1 ; i < len; i++){
    for (int j = 0 ; j < i; j++){
    if (bujian){
    if (v[j] <= v[i] && (record[i] < (record[j]+1)) ){//不减版本
    record[i] = record[j]+1;
    before[i] = j;
    }
    }
    else{
    if (v[j] < v[i] && (record[i] < (record[j]+1)) ){//不减版本
    record[i] = record[j]+1;
    before[i] = j;
    }
    }

    }
    }

    int maxx = -1;
    int finalIndex;

    for (int i = 0 ; i < len; i++){
    if (record[i] > maxx){
    maxx = record[i];
    finalIndex = i;
    }
    }


    while(finalIndex!=-1){
    seq.push_back(v[finalIndex]);
    finalIndex = before[finalIndex];
    }
    reverse(seq.begin(), seq.end());

    // for (int i = 0 ; i < len; i++){
    // cout << record[i]<< " ";
    // }
    // cout << endl;

    return maxx;
    }

    //不减版本
    int binSearch(int s,int e, int value){

    if (e-s == 1){
    if (stack[s] > value){
    return s;
    }
    else{
    return s+1;
    }

    }
    int middle = (e+s)/2;
    if (value == stack[middle]){
    if (bujian) while(value == stack[++middle]){}//这里决定是否要递增,或者不减.注释为递增
    return middle;
    }
    if(value < stack[middle]){
    assert(s!=middle);
    return binSearch(s,middle, value);
    }
    else{
    return binSearch(middle,e, value) ;
    }
    }

    //O(nlog(n))
    int algorithm2(){

    stackSize++;
    stack[stackSize-1] = v[0];

    for (int i = 1 ; i < len; i++){
    if (bujian){
    if(v[i] >= stack[stackSize-1] ){//这里决定是否要递增,或者不减.>为递增,>=为不减
    stackSize++;
    stack[stackSize-1] = v[i];
    }
    else{
    int index = binSearch(0,stackSize,v[i]);
    stack[index] = v[i];
    }
    }
    else{
    if(v[i] > stack[stackSize-1] ){//这里决定是否要递增,或者不减.>为递增,>=为不减
    stackSize++;
    stack[stackSize-1] = v[i];
    }
    else{
    int index = binSearch(0,stackSize,v[i]);
    stack[index] = v[i];
    }
    }

    }

    return stackSize;
    }

    int main(){


    input();
    int a1 = algorithm1();
    int a2 = algorithm2();
    if (a1!=a2){
    cout << "wrong! a1="<<a1<< " a2="<<a2<<endl;
    for (int i = 0 ; i < a1 ; i++){
    cout << seq[i]<<" ";
    }
    }
    else{
    cout <<"max length is " << a1<<endl;
    for (int i = 0 ; i < a1 ; i++){
    cout << seq[i]<<" ";
    }
    }
    //output();
    return 0;
    }

  • 相关阅读:
    Day1_Python基础
    选择排序(java版)
    冒泡排序(java版)
    手写数据库连接池(动态代理)
    JDBC增删查改(使用配置文件)
    JDBC demo
    JSP入门&会话技术
    response实现验证码图片
    android 定制自己的日志工具
    服务的最佳实践——后台执行的定时任务
  • 原文地址:https://www.cnblogs.com/huangshiyu13/p/5876996.html
Copyright © 2011-2022 走看看