最长递增子序列和网易去除最少使从左向右递增又递减问题

(1)最长递增子序列问题

有两种方法:(1)动态规划方法(2)类似二分查找的方法O(nlogn)

动态规划方法:

以i结尾的序列的最长递增子序列和其[0, i - 1]“前缀”的最长递增子序列有关,设LIS[i]保存以i结尾的最长递增子序列的长度:
    若i = 0,则LIS[i] = 1;
    若i > 0,则LIS[i]的值和其[0, i - 1]前缀的最长递增子序列长度有关,用j遍历[0, i -
1]得到其最长递增子序列为LIS[j],对每一个LIS[j],如果序列array[j]  < array[i]并且LIS[j] + 1
> LIS[i],则LIS[i]的值变成LIS[j] + 1。即:
    LIS[i] = max{1, LIS[j] + 1},其中array[i] > array[j] 且 j = [0, i - 1]。

代码如下:

。。。。。晚上补充上

(2)采用类似二分查找方法

假设存在一个序列d[1...9] = 2 1 5 3 6 4 8 9 7,可以看出它的LIS长度是5。
    下面一步一步试着找到它。
    我们定义一个序列B,然后令i = 1 to 9逐个考察这个序列。
    此外,我们用一个变量len来记录现在的最长算到多少。
    首先,把d[1]有序的放到B中,令B[1] = 2,就是说当只有一个数字2的时候,长度为1的LIS的最小末尾是2,这时len = 1;
    然后,把d[2]有序的放到B中,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1] = 2已经没用了,很容易理解吧,这时len = 1;
    接着,d[3] = 5,d[3] > B[1],所以令B[1 + 1] = B[2] = d[3] = 5,就是说长度为2的LIS的最小末尾是5,很容易理解吧,这时B[1...2] = 1, 5,len = 2;
    再来,d[4] = 3,它正好在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时B[1...2] = 1,3,len = 2;
    继续,d[5] = 6,它在3的后面,因为B[2] = 3,而6在3后面,于是很容易推知B[3] = 6,这时B[1...3] = 1,3,6,还是很容易理解吧?这时len = 3;
    第6个,d[6] = 4,你看它在3和6之间,于是就可以把6替换掉,得到B[3] = 4。B[1...3] = 1,3,4,这时len = 3;
    第7个,d[7] = 8,它很大,比4大,于是B[4] = 8,这时len = 4;
    第8个,d[8] = 9,得到B[5] = 9,len继续增大,这时len = 5;
    最后一个,d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] = 7, B[1...5] = 1,3,4,7,9,len = 5。
    于是我们知道了LIS的长度为5。
   
注意,注意。这个1,3,4,7,9不是LIS,它只是存储了对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个
d[9] =
7更新进去对于这个数组数据没有什么意义,但是如果后面再出现两个数字8和9,那么就可以把8更新到d[5],9更新到d[6],得到LIS的长度为6。
    然后应该发现一件事情了:在B中插入数据是有序的,而且进行替换而不需要移动——也就是说,可以使用二分查找,将每一个数字的插入时间优化到O(logn),于是算法的时间复杂度就降低到了O(nlogn)了。

代码如下:

 1 int findlis(int *A,int n,int *lefttoright)     //从左向右最长递增子序列
 2 {
 3     if(A==NULL||n<0)
 4         return -1;
 5     int *lis=new int[n];
 6     //int *lefttoright=new int[n];
 7     lefttoright[0]=1;                    //lefttoright[i]保存从左到右,以i为终点的最长递增子序列长度,注意已经是正常的长度了,不是小一了
 8     int max=0;                           //max是lis[]的最大下标如lis[]={1,2,4}时,max=2;
 9     lis[0]=A[0];
10     for(int i=1;i<n;i++)
11     {
12         int left=0;
13         int right=max;
14         while(left<=right)            //这个二分查找就是最终left落到指定位置例如lis[]={1,2,4},若A[i]=5,left=3(从0开始),则更新为lis[]={1,2,4,5};lis[]={1,2,4},若A[i]=3,left=2,则更新为lis[]={1,2,3};
15         {
16             int mid=(left+right)/2;
17             if(A[i]>lis[mid])
18                 left=mid+1;
19             else
20                 right=mid-1;
21         }
22         lis[left]=A[i];
23         lefttoright[i]=left+1;    //lefttoright[i]等于left加一,同返回时是max+1同样道理
24         if(left>max)          //如果left>max,则让max=left
25             max++;
26     }
27     delete lis;
28     return max+1;             //注意,必须返回max+1,才是最终结果max是最长递增子序列长度减一
29 }

 下面就开始实现“从一列数中筛除尽可能少的数使得从左往右看,这些数是从小到大再从大到小的“这个问题。
    双端LIS问题,用动态规划的思想可以解决,目标规划函数为max{B[i] + C[i] -
1},其中B[i]是从左到右的,0~i个数之间满足递增的数字个数;C[i]为从右到左的,n- 1 ~ i个数之间满足递增的数字个数。最后结果为n
 - max + 1,其中动态规划的时候,可以用二分查找进行处理,如上述求最长递增子序列的方法二。代码如下:
 1 #include <iostream>
 2 using namespace std;
 3
 4 //最长递增子序列的O(nlogn)方法
 5 //lis[i]表示最长递增子序列的长度的i+1的最小的最后一个元素
 6
 7 int findlis(int *A,int n,int *lefttoright)     //从左向右最长递增子序列
 8 {
 9     if(A==NULL||n<0)
10         return -1;
11     int *lis=new int[n];
12     //int *lefttoright=new int[n];
13     lefttoright[0]=1;                    //lefttoright[i]保存从左到右,以i为终点的最长递增子序列长度,注意已经是正常的长度了,不是小一了
14     int max=0;                           //max是lis[]的最大下标如lis[]={1,2,4}时,max=2;
15     lis[0]=A[0];
16     for(int i=1;i<n;i++)
17     {
18         int left=0;
19         int right=max;
20         while(left<=right)            //这个二分查找就是最终left落到指定位置例如lis[]={1,2,4},若A[i]=5,left=3(从0开始),则更新为lis[]={1,2,4,5};lis[]={1,2,4},若A[i]=3,left=2,则更新为lis[]={1,2,3};
21         {
22             int mid=(left+right)/2;
23             if(A[i]>lis[mid])
24                 left=mid+1;
25             else
26                 right=mid-1;
27         }
28         lis[left]=A[i];
29         lefttoright[i]=left+1;    //lefttoright[i]等于left加一,同返回时是max+1同样道理
30         if(left>max)          //如果left>max,则让max=left
31             max++;
32     }
33     delete lis;
34     return max+1;             //注意,必须返回max+1,才是最终结果max是最长递增子序列长度减一
35 }
36
37 int findrighttoleftincrease(int *A,int n,int * righttoleft)  //从右向左最长递增子序列,也可以说成是从左向右最长递减子序列
38 {
39     if(A==NULL||n<0)
40         return -1;
41     int *lis=new int[n];
42     //int *righttoleft=new int[n];
43     lis[0]=A[n-1];           //lis[0]=为A【n-1]
44     righttoleft[n-1]=1;      //注意是lefttoright[n-1]=1
45     int max=0;
46     int left,right;
47     for(int i=n-2;i>=0;i--)
48     {
49         left=0;
50         right=max;
51         while(left<=right)
52         {
53             int mid=(left+right)/2;
54             if(A[i]>lis[mid])
55                 left=mid+1;
56             else
57                 right=mid-1;
58         }
59         lis[left]=A[i];
60         righttoleft[i]=left+1;
61         if(left>max)                  //其实这时,max++后,max==left
62             max++;
63     }
64     delete lis;
65     return max++;
66 }
67
68
69 int main()
70 {
71     //网易的去掉最少元素使得从左向右递增然后递减,即为从左向右递增然后递减的最大值
72     //Big=max(lefttoright[i]+righttoleft[i]-1}
73     //所求即为n-Big。
74     int A[]={2,3,5,1,6,9,10,15,1};
75     int *lefttoright=new int[9];
76     int *righttoleft=new int[9];
77     int maxleft=findlis(A,9,lefttoright);
78     if(maxleft==-1)
79         cout<<"wrong"<<endl;
80     else
81         cout<<"max num lefttoright= "<<maxleft<<endl;
82     int maxright=findrighttoleftincrease(A,9,righttoleft);
83     if(maxright==-1)
84         cout<<"wrong"<<endl;
85     else
86         cout<<"max num righttoleft= "<<maxright<<endl;
87     int max=0;
88     for(int i=0;i<9;i++)
89     {
90         if(lefttoright[i]+righttoleft[i]-1>max)
91             max=lefttoright[i]+righttoleft[i]-1;
92     }
93     cout<<"去除"<<9-max<<endl;
94     delete lefttoright;
95     delete righttoleft;
96     system("pause");
97 }

完。

最长递增子序列和网易去除最少使从左向右递增又递减问题

时间: 2024-10-20 05:21:40

最长递增子序列和网易去除最少使从左向右递增又递减问题的相关文章

在一个二维数组中,每一行都按照从左到右递增的顺序排序

/*  * 在一个二维数组中,  * 每一行都按照从左到右递增的顺序排序,  * 每一列都按照从上到下递增的顺序排序.  * 请完成一个函数,输入这样的一个二维数组和一个整数,  * 判断数组中是否含有该整数.  */ public static void main(String[] args) { int[][] array = {{1,2,3},{4,5,6},{7,8,9}}; System.out.println(Find2(1, array)); } /*  * 思路一:暴力遍历法  

在一个二维数组中,每一行都从左到右递增,每一列都从上到下递增,在这样一个数组里查找一个数

第一种: 思路: 二维数组是从左到右递增,从上到下递增,row为行数,col为列数,a[row-1][col-1]为最大数,如果剔除这一行,这一列,则最大数为a[row-2][col-2],如果要查找的num大于a[row-2][col-2]并且大于a[row-1][col-1],则在该row-1行和col-1列查找,照这样遍历. 这样太麻烦,效率太低,应当舍弃. #include<stdio.h> #include<stdlib.h> #include<assert.h&g

剑指offer系列——二维数组中,每行从左到右递增,每列从上到下递增,设计一个算法,找其中的一个数

题目:二维数组中,每行从左到右递增,每列从上到下递增,设计一个算法,找其中的一个数 分析: 二维数组这里把它看作一个矩形结构,如图所示: 1 2 8 9 2 4 9 12 4 7 10 13 6 8 11 15 在做这道题的时候我最先考虑的是每次比较对角线上的元素可能可以取得较好的效果, 以查找9为例, 从1(0,0)开始,1<10,可以得出结论,10在1的右侧或下侧: 1 2 8 9 2 4 9 12 4 7 10 13 6 8 11 15 然后看4(1,1),4<9, 1 2 8 9 2

【二】在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否函数该整数。 ```java public class Test { /** 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否函数该整数. public class Test { /** 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否函数该整数. */ public static void main(String[] args) { int[][] arr =

在一个每一行从左到右递增每一列从上到下递增的二维数组中查找一个整数是否存在——3

给定一个二维数组,数组的特点是:每一行从左到右数据大小依次递增,每一列数据从上到下依次递增,要求判断一个整数是否在这个二维数组中: 设计二维数组如下: 首先,毋庸置疑的的是,遍历一遍数组肯定能判断出来,这也是最笨的方法了,因此,要想提高程序的运行效率就得找出一种高效的方法来查找: 一开始的想法大概都能想到从数组第一行第一列的数开始沿着对角线判断,如果是对角线数据就可以直接返回,比如我们要想查找17这个数,这时候17比0.9.16都要大,因此以0.9.16为对角线的矩形数据就可以排除了,接下来判断

【动态规划】拦截导弹_dilworth定理_最长递增子序列

问题 K: [动态规划]拦截导弹 时间限制: 1 Sec  内存限制: 256 MB提交: 39  解决: 10[提交][状态][讨论版] 题目描述 张琪曼:“老师,修罗场是什么?” 墨老师:“修罗是佛家用语,修罗毕生以战斗为目标,修罗场指的是他们之间的死斗坑,人们通常用‘修罗场’来形容惨烈的战场.后来又引申出‘一个人在困境中做绝死奋斗’的意思.所以,这其实也在暗示我们,即使是身处绝境,也不要放弃奋斗.再说了,情况其实没有这么糟糕,因为我们最新的导弹拦截系统已经研制好了.” 魔法世界为了防御修罗

最长递增子序列(LIS)求解

问题描述 最长递增子序列也称 "最长上升子序列",简称LIS ( longest increasing subsequence).设L=<a1,a2,-,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lis=<ak1,ak2,-,akm>,其中k1<k2<-<km且ak1<ak2<-<akm.求最大的m值. 如:求一个一维数组arr[i]中的最长递增子序列的长度,如在序列{ 7, 1, 6, 5, 3, 4, 8

最长递增子序列 &amp;&amp; 最大子序列、最长递增子序列、最长公共子串、最长公共子序列、字符串编辑距离

http://www.cppblog.com/mysileng/archive/2012/11/30/195841.html 最长递增子序列问题:在一列数中寻找一些数,这些数满足:任意两个数a[i]和a[j],若i<j,必有a[i]<a[j],这样最长的子序列称为最长递增子序列. 设dp[i]表示以i为结尾的最长递增子序列的长度,则状态转移方程为: dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i]. 这样简单的复杂度为O(n^2),其实还有更好的方

[ACM] 九度OJ 合唱队形 (最长递增子序列改版)

题目1131:合唱队形 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1680 解决:520 题目描述: N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, -, K,他们的身高分别为T1, T2, -, TK, 则他们的身高满足T1 < T2 < - < Ti , Ti > Ti+1 > - > TK (1 <= i <=