shuoj1936-D序列—最长上升子序列

Description

已知两个长度为N的数组A和B。下标从0标号至N-1。

如今定义一种D序列 (如果长度为L)。这样的序列满足下列条件:

1. 0 <= D[i] <= N-1

2. A[D[i]] < A[D[i+1]]  (0 <= i < L-1)

3. B[D[i]] > B[D[i+1]]  (0 <= i < L-1)

求满足条件的D序列的最大长度。

(事实上这样的序列叫做D序列的原因是:这道题是D题)

Input

多组数据,每组数据第一行是一个整数N。

(1 <= N <= 100000)

第二行中有N个正整数A[0]~A[N-1]。

第三行中有N个正整数B[0]~B[N-1]。

保证全部输入整数在int范围内。

Output

对每组数据。输出一个整数。表示D序列的最大长度L。

Sample Input

3

1 1 2

3 2 1

3

1 2 3

3 2 1

4

1 3 2 4

3 1 2 4

Sample Output

233

思路::将A数组,B数组以A为第一keyword,B为第二keyword进行升序排序。然后将B倒置求B的最长上升子序列。

为了避免下标排序和写比較函数。将A B 保存在pair里先排序,然后再取出来存放大到 A 中。倒置,并求最长子序列。

在求最长上升子序列时,直接用dp的方法时间复杂度为 O(n^2),会超时,所以採用其它的方法求。

方法(1)::利用lower_bound 求上升子序列O(nlogn)

//lower_bound三个參数分别为要比較的起始点地址,终止点的地址+1(也就是左闭右开)。要比較的值(如果为d)。

//它的作用是返回一个地址。这个地址是在比較的范围内>=d的最小的值的地址。

//举个样例,a[] = {0 , 1 ,2, 4, 5, 7 } p =lower_bound(a,a+6,3),p就为 4 的地址。假设p =lower_bound(a,a+6,4),p也为 4 的地址

方法(2)::利用二分法求上升子序列O(nlogn)

利用lower_bound要在数组中进行比較,当要比較的数较大时。无法将数存放在数组中。而利用二分法能解决这一问题,但代码难度较大。

两种方法的思路是一样的。将数组A中子序列长度为 i 的最小值存放在数组S中。我们以3 2 4 6  5 7 3 为例进行演示行为遍历。列为数组S,变化的地方已经标出来。有助于理解。

在这里a[ i ] > s[ j ]&&a[i]<=s[ j + 1 ]就应该把a[ i ]放在s[
j+1 ]的位置。

所以关键就是找出 j 就知道把a[ i ]放在哪了。

上面的两种方法就是用来寻找 j的 。(在这里lower_bound直接返回 j + 1 )

我们能够发现s数组中的值必定是有序递增的。这也是能够利用二分法的一个必要条件。

演示
0 1 2 3 4
1 3      
2 2      
3 2 4    
4 2 4 6  
5 2 4 5  
6 2 4 5 7
7 2 3 5 7
         

方法(1)代码::

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
int s[100005];
vector<pair<int,int> > T;//能够用vector存,也能够直接用数组 pair<int ,int> T[100005];

int main()
{
    int n;
    while(~scanf("%d",&n)){
        T.clear();//假设不初始或要出错用数组就不须要了
        for(int i = 0;i<n;i++)scanf("%d",&a[i]);
        for(int i = 0;i<n;i++)scanf("%d",&b[i]);
        //假设用数组应该为T[i] = {a[i],b[i]};
        for(int i = 0;i<n;i++)T.push_back(make_pair(a[i],b[i]));
        //sort(T,T+n);
        std::sort(T.begin(),T.end());//排序
        for(int i= 0;i<n;i++)a[i] = T[i].second;//把排序后的数组b取出来放到a中
        reverse(a,a+n);//导致
        int len = 1; s[1] = a[0];//<span style="font-family: Arial, Helvetica, sans-serif;">第一个元素首先放入 s[1]</span>
        for(int i = 1;i<n;i++){//dp的思想,逐个将a中元素增加s.
            int t = a[i];
            if(t>s[len])s[++len] = a[i];
            else{
                int p = lower_bound(s+1,s+len+1,t)-s;
                s[p] = t;
            }
        }
        printf("%d\n",len);
    }
    return 0;
}

方法(2)代码::

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
int s[100005];
vector<pair<int,int> > T;

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        T.clear();
        for(int i = 0;i<n;i++)scanf("%d",&a[i]);
        for(int i = 0;i<n;i++)scanf("%d",&b[i]);
        for(int i = 0;i<n;i++)T.push_back(make_pair(a[i],b[i]));
        std::sort(T.begin(),T.end());
        for(int i= 0;i<n;i++)a[i] = T[i].second;
        reverse(a,a+n);
        int len = 1;s[1] = a[0];
        for(int i = 1;i<n;i++){
            int t = a[i];
            if(t>s[len]) s[++len] = a[i];
            else{
                int l = 1,r = len,mid;
                int ans = 0;
                while(l<=r)//这里的二分法採用了左闭右闭的思路
                {
                    mid = (r+l)/2;
                    if(s[mid]<t){
                        l = mid+1;
                        ans=max(ans, mid);//ans即为思路中的j,j必定为s数组中小于t的最大的数
                    }
                    else r = mid-1;
                }
                s[ans+1] = t;
            }
        }
        printf("%d\n",len);
    }
    return 0;
}
时间: 2024-08-18 05:07:38

shuoj1936-D序列—最长上升子序列的相关文章

蓝桥杯 最长公共子序列

给定俩个字符串,S1S2.....Sn和T1T2......Tn.求出这俩个字符串中最长的公共子序列的长度.字符串S1S2......Sn的子序列指可以表示Si1Si2.......Sim的序列 /* *最长公共子序列 ,输入俩个序列之后不断检查是否有相同出现 *如果发现字符相同,则在动态记录数组中加 1 *如果数组不相同,则选择记录 a数组减 1最大,或者选择 b数组子序列 减 1最大 * */ #include<stdio.h> #include<string.h> int N

(hdu step 3.2.4)FatMouse&#39;s Speed(在第一关键字升序的情况下,根据第二关键字来求最长下降子序列)

在写题解之前给自己打一下广告哈~..抱歉了,希望大家多多支持我在CSDN的视频课程,地址如下: http://edu.csdn.net/course/detail/209 题目: FatMouse's Speed Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1034 Accepted Submission(s): 526   Proble

(hdu step 3.2.6)Monkey and Banana(在第一关键字有序的情况下,根据第二关键字求最长上升子序列的高度之和)

题目: Monkey and Banana Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 720 Accepted Submission(s): 455   Problem Description A group of researchers are designing an experiment to test the IQ of a m

线性动态规划——解最长公共子序列问题

动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加(分治思想,递归方法.往往会由于数据大导致递归层次过多而超时或爆栈,即使采用记忆化等优化策略,仍然可能解决不了问题). 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,找出数组中相关元素之间存在的关系(动态转移方程),这就是动态规划法所采用的基本方法. [

【模拟】CSU 1807 最长上升子序列~ (2016湖南省第十二届大学生计算机程序设计竞赛)

题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1807 题目大意: 给你一个长度为N(N<=105)的数列,数列中的0可以被其他数字替换,最终形成一个1~N的排列,求这个排列的最长上升子序列长度为N-1的方案数. 题目思路: [模拟] 这道题需要分类讨论. 首先可以肯定,一个长度为n的序列最长上升子序列长度为n-1(最长下降子序列长度为2),那么这个序列的样子是1~n从小到大排列后其中一个数字挪到其余数字中间(错位) 一个长度为L的

求解两个序列的所有最长公共子序列(LCSes)

 摘要 本篇博文提供了实现求解所有最长公共子序列的程序实现,并提供输出所有公共子序列的方法解释,需要具备基础知识是求解一个公共子序列的动态规划方法,请自行查阅相关资料. 题目重述 子序列概念:设X=< x1, x2,┅, xm>,若有1≤i1< i2< ┅ <ik≤m,使得Z=< z1, z2,┅, zk> = < xi1, xi2,┅, xik>,则称Z是X的子序列,记为Z<X. 例如: X=<A,B,C,B,D,A,B>, 

Longest Ordered Subsequence与最少拦截系统 DP+贪心(最长上升子序列及最少序列个数)

Longest Ordered Subsequence A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence ( a1, a2, ..., aN) be any sequence ( ai1, ai2, ..., aiK), where 1 <= i1 < i2 < ... < iK <= N.

【动态规划】【最长上升子序列】【贪心】bzoj1046 [HAOI2007]上升序列

nlogn求出最长上升子序列长度. 对每次询问,贪心地回答.设输入为x.当前数a[i]可能成为答案序列中的第k个,则若 f[i]>=x-k && a[i]>ans[k-1] 即可. f[i]表示以a[i]开头的最长上升子序列长度. 但这个东西难以统计.so 我们将原序列反序,求f[i] 表示以 a[i]为结尾的最长下降子序列长度即可.最后再将f.a reverse一下. 1 #include<cstdio> 2 #include<algorithm> 3

[csu/coj 1078]多个序列的最长公共子序列

题意:给n个序列,同一个序列里面元素互不相同,求它们的最长公共子序列. 思路:任取一个序列,对于这个序列里面的两个数ai,aj(i<j),如果对于其它每一个序列,都出现过ai,aj,且ai在aj之前出现,那么i到j连一条长度为1的有向边,完美转化为DAG最长路.需要注意:对于某个数,如果某个序列没出现那么这个点的答案应该为-INF,表示这个点表示的状态不合法. 代码: 1 #pragma comment(linker, "/STACK:10240000,10240000") 2