题意:求使双端队列不递减的最长长度

对所有i=1,2,……n

ans=(以i为起点的,下同)最长不下降(长度为len1)+最长不上升(长度为len2)
     -min(最长不下降中数a[i]至少有k1个,最长不上升中数a[i]至少有k2个)
计算k1、k2时包含本身的a[i],故k1,k2至少为1

下面简单证明下
记s=a[i],k1个s一定是从i开始往后(包含i)的前k1个s。

(若不是,一定可以在中间找到一个漏掉的s,而接上这个s可以构成长度更长的最长不下降)。

同理k2个s一定是从i开始往后(包含i)的前k2个s。那么有min(k1,k2)个s重复,求最后ans自然要剪掉重复的。

于是dp1[i]代表从n开始(从右向左),以i为结尾的最长不上升的长度
com1[i]代表
构成以i结尾的dp[i]的长度串至少需要i几个(至少为1)
那com1怎么求呢?

a[]为读入的数组
nlgn求最长不上升的方法是维护g[0]~g[len],g[i]=max{a[x]};其中dp[x]=i;
边界g[0]=+inf。故g[i]数组是不递增的。
对下文给出的第一组数据进行模拟g
1
1 1
1 1 1
1 1 1 1
1 1 1 1 1 
1 1 1 1 1 1
4 1 1 1 1 1
4 3 1 1 1 1
4 3 2 1 1 1    
4 3 2 1 1 1 1

回到com1数组
如果g数组里的i少一个呢,dp[i]是否就要-1了
如果g数组里的i全没了,dp[i]是否就是从n开始(从右向左),以i为结尾的最长不上升的长度中减去该串至少有几个a[i]
那么com1数组怎么求?每次更新完g查找一下g数组有几个a[i](二分左边界和右边界)

dp2和com2求法类似。

给出几组数据

10
1 2 3 4 1 1 1 1 1 1
ans=10
6
5 5 6 6 3 3
ans=6

#include<stdio.h>
#define N 100010
#define inf 1000000000
int len,n,ans;
int a[N],d[N],dp1[N],dp2[N],com1[N],com2[N];int find1(int x)
{ int l=1,r=len,mid,ans=0;while (r>=l){ mid=(l+r)/2;if (d[mid]>=x) {if (mid>ans)ans=mid;l=mid+1;} else r=mid-1;}return ans;
}
int find2(int x)
{ int l=1,r=len,mid,ans=0;
while (r>=l){ mid=(l+r)/2;if (d[mid]<=x) {if (mid>ans)ans=mid;l=mid+1;} else r=mid-1;}return ans;
}
int find_1_left(int x)
{   int l=0,r=len,mid,ans=-1;while (r>=l){mid=(l+r)/2;if (d[mid]==x) ans=mid;if (d[mid]>x) l=mid+1;else r=mid-1;}return ans;
}
int find_1_right(int x)
{   int l=0,r=len,mid,ans=-1;while (r>=l){mid=(l+r)/2;if (d[mid]==x) ans=mid;if (d[mid]>=x) l=mid+1;else r=mid-1;}return ans;
}
int find_2_left(int x)
{   int l=0,r=len,mid,ans=-1;while (r>=l){mid=(l+r)/2;if (d[mid]==x) ans=mid;if (d[mid]<x) l=mid+1;else r=mid-1;}return ans;
}
int find_2_right(int x)
{   int l=0,r=len,mid,ans=-1;while (r>=l){mid=(l+r)/2;if (d[mid]==x) ans=mid;if (d[mid]<=x) l=mid+1;else r=mid-1;}return ans;
}
int min(int a,int b)
{return a>b?b:a;
}
void doit()
{   int i;scanf("%d",&n);for (i=1;i<=n;i++) scanf("%d",&a[i]);d[0]=inf; len=0;for (i=n;i>=1;i--){if (a[i]<=d[len]) {d[++len]=a[i]; dp1[i]=len; }else {int tmp=find1(a[i])+1;d[tmp]=a[i]; dp1[i]=tmp; }com1[i]=find_1_right(a[i])-find_1_left(a[i])+1;}d[0]=-inf; len=0;for (i=n;i>=1;i--){if (a[i]>=d[len]) {d[++len]=a[i]; dp2[i]=len; }else {int tmp=find2(a[i])+1;d[tmp]=a[i]; dp2[i]=tmp; }com2[i]=find_2_right(a[i])-find_2_left(a[i])+1;}/*  for (i=1;i<=n;i++)printf("%d ",dp1[i]);printf("\n");for (i=1;i<=n;i++)printf("%d ",com1[i]);printf("\n");for (i=1;i<=n;i++)printf("%d ",dp2[i]);printf("\n");for (i=1;i<=n;i++)printf("%d ",com2[i]);printf("\n");*/ans=0;for (int i=1;i<=n;i++){int tmp=dp1[i]+dp2[i]-min(com1[i],com2[i]);if (tmp>ans) ans=tmp;}printf("%d\n",ans);}int main()
{   int cas;scanf("%d",&cas);while (cas--) doit();return 0;
}

upper_bound写法

#include<stdio.h>
#include<algorithm>
#define N 100010
#define inf 1000000000
using namespace std;
int len,n,ans;
int a[N],d[N],dp1[N],dp2[N],com1[N],com2[N];
void doit()
{   int i;scanf("%d",&n);for (i=1;i<=n;i++) scanf("%d",&a[i]);d[0]=-inf; len=0;for (i=n;i>=1;i--){if (a[i]>=d[len]) {d[++len]=a[i]; dp2[i]=len; }else {int tmp=lower_bound(d,d+len+1,a[i]+1)-d;d[tmp]=a[i];dp2[i]=tmp;}com2[i]=(lower_bound(d,d+len+1,a[i]+1)-d-1)-(lower_bound(d,d+len+1,a[i])-d)+1;}d[0]=-inf; len=0;for (i=n;i>=1;i--) a[i]=-a[i];for (i=n;i>=1;i--){if (a[i]>=d[len]) {d[++len]=a[i]; dp1[i]=len; }else {int tmp=lower_bound(d,d+len+1,a[i]+1)-d;d[tmp]=a[i];dp1[i]=tmp;}com1[i]=(lower_bound(d,d+len+1,a[i]+1)-d-1)-(lower_bound(d,d+len+1,a[i])-d)+1;}ans=0;for (int i=1;i<=n;i++){int tmp=dp1[i]+dp2[i]-min(com1[i],com2[i]);if (tmp>ans) ans=tmp;}printf("%d\n",ans);
}int main()
{   int cas;scanf("%d",&cas);while (cas--)doit();return 0;
}

Hdu 4604 DP相关推荐

  1. HDU 5928 DP 凸包graham

    给出点集,和不大于L长的绳子,问能包裹住的最多点数. 考虑每个点都作为左下角的起点跑一遍极角序求凸包,求的过程中用DP记录当前以j为当前末端为结束的的最小长度,其中一维作为背包的是凸包内侧点的数量.也 ...

  2. hdu 1171 dp(多重背包)

    View Code //hdu 1171 dp(多重背包)//题意:把所有物品的价值尽量分为相等的两份,不能等分的话 //后面那份可以稍小于前面的 //求出价值总和后,令价值的一半为背包容量,让背包尽 ...

  3. HDU 4832(DP+计数问题)

    HDU 4832 Chess 思路:把行列的情况分别dp求出来,然后枚举行用几行.竖用几行,然后相乘累加起来就是答案 代码: #include <stdio.h> #include < ...

  4. hdu 5086(dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5086 题目大意:给出长度为n的数组,然后要求累计里面的每个子串的和. 解题思路:这道题直接枚举肯定不行 ...

  5. hdu 2059(dp)

    龟兔赛跑 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Problem Des ...

  6. HDU 3646 DP + 二分

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=3646 题意:你有N把武器,每把武器可以对敌人造成一定的伤害(et:攻击力500,敌人血量为200,杀死敌人 ...

  7. Hdu 4293 DP

    题意: n个人说自己前面有多少人 后面有多少人 求出说真话人数最多的情况 每个样例有 一个 n 表示n个人 接下来 n 行有a b 表示他前面的人数和后面的人数 思路: 如果已经知道了其中一组的人数~ ...

  8. hdu 3905(dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3905 思路:dp[i][j]表示前i分钟,睡了j分钟收获的的最大价值,并记tmp_dp[i][j]为从 ...

  9. hdu 4472 dp

    http://acm.hdu.edu.cn/showproblem.php?pid=4472 第一直觉找规律,第二直觉 把树拆成子树,然后递推或者DP 然后发现不行,然后才发现自己题都没读,,,, d ...

最新文章

  1. 随机洗牌算法 银行家算法
  2. 网站SEO优化中导入链接有哪些作用?
  3. MySQL非空约束(NOT NULL)
  4. Android studio 克隆分支
  5. c#中程序以管理员身份运行的三种办法
  6. 广东计算机学会 信息学省初赛,报名丨2019第36届全国信息学奥林匹克竞赛于广州二中举办!...
  7. Vim安装YouCompleteMe自动补全插件
  8. Ubuntu18.04构建Go语言项目
  9. Halcon深度学习预训练网络模型
  10. SPSS多元线性回归及逐步回归学习笔记
  11. 微信分享代码申请key教程
  12. Wind7外接显示器选择拓展模式后,鼠标只能往右移动才能切换到外接显示器上,不能修改切换方向...
  13. 在這個神奇的國度找個正常點兒的DNS都很困難
  14. mini-itx PC:推测Intel D525MW支持UEFI
  15. 大恒相机+VS开发+Qt 跑通示例工程
  16. 微信小程序评分功能(一)
  17. 对ABAP程序调优的学习(三)并行并发读取
  18. 一文读懂麦克风典型应用电路
  19. STM32的硬件I2C与AT24C16
  20. 1款免费炫酷的javaweb版报表工具

热门文章

  1. 【Luat-air105】2.2 lvgl显示图片bin格式
  2. MES退料管理:优化生产流程的重要环节
  3. extj.js学习笔记
  4. rman备份恢复详细方案和介绍
  5. 虚拟内存详解之一 内存的使用和重定位
  6. 【数字图像处理与应用】模板匹配
  7. MAKEWORD、HIWORD、LOWORD 宏定义
  8. docker启动异常 prior storage driver overlay2 failed: driver not supported
  9. linux系统kate,linux kate 终端不能用,版本是centos,就是不能在kate下方直接编译源程序,没有shell界面,设置也不行...
  10. 采购管理三大误区及提高采购工作效率的十大方法