csu 1551(线段树+DP)

1551: Longest Increasing Subsequence Again

Time Limit: 2 Sec  Memory Limit: 256 MB
Submit: 267  Solved: 112
[Submit][Status][Web Board]

Description

Give
you a numeric sequence. If you can demolish arbitrary amount of
numbers, what is the length of the longest increasing sequence, which is
made up of consecutive numbers? It sounds like
Longest Increasing Subsequence at first sight. So, there is another
limitation: the numbers you deleted must be consecutive.

Input

There are several test cases.
For each test case, the first line of input contains the length of
sequence N(1≤N≤10^4). The second line contains the elements of
sequence——N positive integers not larger than 10^4.

Output

For each the case,
output one integer per line, denoting the length of the longest
increasing sequence of consecutive numbers, which is achievable by
demolishing some(may be zero) consecutive numbers.

Sample Input

7
1 7 3 5 9 4 8
6
2 3 100 4 4 5

Sample Output

4
4

题意:给出一个序列,删除任意一段连续的数(也可以不删除),删完后最长严格递增子段(序列必须是连续的)最长。题解:对付这种题的能力不行啊,看了题解做的.分析:dp[i][0]代表未删除时以i结尾最长连续上升子序列长度,dp[i][1]代表删除时以i结尾最长连续上升子序列长度不删除时很好处理,递推过去就好了,所以可以预处理 dp[i][0]dp[i][1]可以从dp[i][0]转移过来1.dp[i][1] = dp[i-1][1]+1 (a[i]>a[i-1])2.dp[i][1] = dp[j][0]+1 (a[i]>a[j]&&i>j) 这里找dp[j][0]可以利用线段树维护.这里的更新过程和查询与此题有异曲同工之妙 http://www.cnblogs.com/liyinggang/p/5656485.html
/**分析:dp[i][0]代表未删除时以i结尾最长连续上升子序列长度,
dp[i][1]代表删除时以i结尾最长连续上升子序列长度
不删除时很好处理,递推过去就好了,所以可以预处理 dp[i][0]
dp[i][1]可以从dp[i][0]转移过来
1.dp[i][1] = dp[i-1][1]+1 (a[i]>a[i-1])
2.dp[i][1] = dp[j][0]+1 (a[i]>a[j]&&i>j)
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define N 10005
int dp[N][2],a[N],b[N];
int tree[N<<2],MAX;
void pushup(int idx)
{
    tree[idx] = max(tree[idx<<1],tree[idx<<1|1]);
}
void build(int l,int r,int idx)
{
    if(l==r)
    {
        tree[idx] = 0;
        return;
    }
    int mid = (l+r)>>1;
    build(l,mid,idx<<1);
    build(mid+1,r,idx<<1|1);
    pushup(idx);
}
void update(int pos,int v,int l,int r,int idx)
{
    if(l==r)
    {
        tree[idx] = max(tree[idx],v);
        return;
    }
    int mid = (l+r)>>1;
    if(pos<=mid) update(pos,v,l,mid,idx<<1);
    else update(pos,v,mid+1,r,idx<<1|1);
    pushup(idx);
}
void query(int l,int r,int L,int R,int idx)
{
    if(l>=L&&R>=r)
    {
        MAX = max(MAX,tree[idx]);
        return;
    }
    int mid = (l+r)>>1;
    if(mid>=L)  query(l,mid,L,R,idx<<1);
    if(mid<R)  query(mid+1,r,L,R,idx<<1|1);
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            b[i] = a[i];
        }
        sort(b+1,b+1+n);
        int cnt = unique(b+1,b+1+n)-b;
        for(int i=1; i<=n; i++) a[i] = lower_bound(b+1,b+cnt,a[i]) - b;
        for(int i=1; i<=n; i++)
        {
            dp[i][0] = 1;
            if (i > 1 && a[i]>a[i - 1])
                dp[i][0] = dp[i - 1][0] + 1;
        }
        build(1,n,1);
        int ans = -1;
        for(int i=1; i<=n; i++)
        {
            dp[i][1] = 1;
            if(i>1&&a[i]>1)
            {
                if(a[i]>a[i-1]) dp[i][1] = dp[i-1][1]+1;
                MAX = -1;
                query(1,n,1,a[i]-1,1); /**找到最大的 dp[j][0] 满足 a[j]<a[i] && j<i,有必要解释一下这里
                    为什么是更新1~a[i]-1呢?因为我们找的是按照大小排名来的数,而不是,比如说 1 3 5 6 4
                    假设我找的是 a[5] ,所以我应该忽略中间的 5 6 ,直接找 1 3 .而我们的枚举顺序也保证了 j<i 这个条件.
                    个人觉得很巧妙.
                */
                dp[i][1] = max(dp[i][1],MAX+1);
            }
            update(a[i],dp[i][0],1,n,1);/**
                更新过程同样巧妙,也是更新的按照大小排名来的顺序,不要与下标弄混了
            */
            ans = max(ans,max(dp[i][1],dp[i][0]));
        }
        printf("%d\n",ans);
    }
    return 0;
}
时间: 2024-10-10 17:13:09

csu 1551(线段树+DP)的相关文章

hdu 3016 Man Down (线段树 + dp)

题目大意: 是男人就下一般层...没什么可以多说的吧. 注意只能垂直下落. 思路分析: 后面求最大值的过程很容易想到是一个dp的过程 . 因为每一个plane 都只能从左边 从右边下两种状态. 然后我们所需要处理的问题就是 ,你如何能快速知道往左边下到哪里,往右边下到哪里. 这就是线段树的预处理. 讲线段按照高度排序. 然后按照高度从小到大加入到树中. 然后去寻找左端点 和 右端点最近覆盖的线段的编号. #include <cstdio> #include <iostream> #

ZOJ3632 线段树+DP

买西瓜吃,每个西瓜有两个参数,一个是p代表价格,一个是t代表能吃几天,要求n天每天都能吃西瓜,而且如果你今天买了,以前买的还没吃完 那么都得扔了,求最小花费,还真想不到用线段树+DP,最后看了一下别人的标题,想了一下,DP方程挺好推的,线段树也只是单点查询, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #inclu

HDU6447 YJJ&#39;s Salesman 2018中国大学生程序设计竞赛 - 网络选拔赛1010 离散化+线段树+DP

YJJ's Salesman Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 253    Accepted Submission(s): 62 Problem Description YJJ is a salesman who has traveled through western country. YJJ is always on

[HDU 6447][YJJ&#39;s Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6447 题意: 左上角(0,0),右下角(10^9,10^9)的网格,其中有n(1<=n<=10^5)个方格内有权值. 一次只能沿右,下,右下三个方向走一个格子,只有沿右下方向走到格子里才可以获得权值. 问从(0,0)到(10^9,10^9)的路径最大权值是多少. 思路: 网格路径权值问题,第一感考虑DP,x从上往下,y从左往右刷表,状态转移方程为dp[i][j]=max(dp[i-1][j],dp[

CF932F(李超线段树+dp)

CF932F(李超线段树+dp) 此题又是新玩法, 李超线段树合并优化\(dp\) 一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价 \(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\) 如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线

【BZOJ1835】[ZJOI2010]base 基站选址 线段树+DP

[BZOJ1835][ZJOI2010]base 基站选址 Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄不超过Si的范围内建立了一个通讯基站,那么就成它被覆盖了.如果第i个村庄没有被覆盖,则需要向他们补偿,费用为Wi.现在的问题是,选择基站的位置,使得总费用最小. 输入数据 (base.in) 输入文件的第一行包含两个整数N,K,含义如上所述. 第

hdu4521(线段树+dp)

传送门:小明系列问题——小明序列 题意:有n个数,求间距大于d的最长上升序列. 分析:dp[i]表示在i点以a[i]结束距离大于d的最长上升序列,然后每更新到第i点时,取i-d之前小于a[i]的数为结束的最长上升序列进行状态转移,并维护前i-d之前的最大上升序列,维护i-d之前的每点为结束的最长上升序列用线段树维护即可. #pragma comment(linker,"/STACK:1024000000,1024000000") #include <cstdio> #inc

lightoj1085 线段树+dp

1 //Accepted 7552 KB 844 ms 2 //dp[i]=sum(dp[j])+1 j<i && a[j]<a[i] 3 //可以用线段树求所用小于a[i]的dp[j]的和 4 //需要离散化 5 #include <cstdio> 6 #include <cstring> 7 #include <iostream> 8 #include <queue> 9 #include <cmath> 10 #

codeforces#426(div1) B - The Bakery (线段树 + dp)

题意:把 n 个数划分成 m 段,要求每组数不相等的数的数量最大之和. 思路: dp方程 : dp[i][j] = max( dp[k][j-1] + v(k, i) );( j<=k<i , k = j, j+1, +...+ i-1) dp[i][j]表示第 i 个数分到第 j 段的最大值. v(k, i) 表示k~i中不同数的个数,此处用hash记录每个数上一次出现的位置,从上一次出现的位置到当前位置的 dp[i][j-1] 值均可+1. 此时时间复杂度 O(n*m*log(n)). 线