小P的单调区间——解题报告
题目链接:
http://172.25.37.251/problem/115
题目大意:
给定一个序列,选出若干个数,将其分成若干单调的子序列(可不连续),相邻序列单调性不同,第一个序列一定为单调递增。求出所有方案中序列和的平均值的最大值。如 3 3 3, 7 7 7, 9 9 9, 2 2 2, 4 4 4, 5 5 5,把它划分为 [ 3 , 7 , 9 ] [3,7,9] [3,7,9], [ 2 , 4 ] [2,4] [2,4], [ 5 ] [5] [5],答案为: ( 3 + 7 + 9 + 2 + 4 + 5 ) / 3 = 10 (3+7+9+2+4+5)/3=10 (3+7+9+2+4+5)/3=10。把它划分为 [ 3 , 9 ] [3,9] [3,9], [ 5 ] [5] [5],答案为: ( 3 + 9 + 5 ) / 2 = 8.5 (3+9+5)/2=8.5 (3+9+5)/2=8.5。
题目分析:
1.我们很明显可以通过枚举选取第 i i i个点时,共分成了 j j j个序列来进行转移,那么我们可以在枚举上一个选择的数来进行转移。我们就可以愉快的暴力 D P DP DP了。如下:
//dp[i][j]表示选择第i个数后,分为j个单调序列时,数的总和的最大值
for(ll i=1;i<=n;i++)//枚举当前选择的数
{for(ll j=1;j<=n;j++)//枚举分成了多少个序列{for(ll k=0;k<i;k++)//枚举上一个选择的点{if(j%2==0)//当前序列要求单调递减{if(a[k]>a[i])//满足递减,可继承dp[k][j]的值,加入第j个序列中dp[i][j]=max(dp[i][j],max(dp[k][j],dp[k][j-1])+a[i]);elsedp[i][j]=max(dp[i][j],dp[k][j-1]+a[i]);}else//当前序列要求单调递增{if(a[k]<a[i])//满足递增,可继承dp[k][j]的值,加入第j个序列中dp[i][j]=max(dp[i][j],max(dp[k][j],dp[k][j-1])+a[i]);elsedp[i][j]=max(dp[i][j],dp[k][j-1]+a[i]);}}ans=max(ans,dp[i][j]/(double)j);}
}
2.很显然的一点是这样的做法复杂度为 O ( n 3 ) O(n^3) O(n3),是不能通过此题的,我们需要更优的方法。我们可以发现,无论能不能继承前一个 d p dp dp值时,我们都要算上将这个数单独作为一个序列时的值。因为 k < i k<i k<i,每一个 k k k都要参与计算,那么很明显我们可以利用一个前缀和来优化这个过程。如下:
for(ll j=1;j<=n;j++)//枚举序列数
{for(ll i=1;i<=n;i++){f[i][j]=f[i-1][j];dp[i][j]=max(dp[i][j],f[i-1][j-1]+a[i]);for(ll k=0;k<i;k++){if(j%2==0 && a[k]>a[i])dp[i][j]=max(dp[i][j],dp[k][j]+a[i]);else if(j%2!=0 && a[k]<a[i])dp[i][j]=max(dp[i][j],dp[k][j]+a[i]); }f[i][j]=max(f[i][j],dp[i][j]);ans=max(ans,dp[i][j]/(double)j);}
}
3.虽然我们用前缀和来优化,但是我们并没有解决时间复杂度的问题。我们通过分析可以发现,枚举k的这一维,由于不具备单调性,所以不存在 O ( 1 ) O(1) O(1)的转移方式。但是我们很快可以发现,这个地方是可以利用线段树来维护的,每次查询根据j的奇偶性选择查询 [ 0 , a [ i ] ) [0,a[i]) [0,a[i])还是 ( a [ i ] , M a x ] (a[i],Max] (a[i],Max],每次将得到的 d p [ i ] [ j ] dp[i][j] dp[i][j]插入线段树。(由于数据范围的问题,这里需要离散化)。现在的时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)。
4.继续分析我们发现我们无法优化枚举 i i i的过程,但要通过 n ≤ 100000 n \leq 100000 n≤100000的数据,我们需要O(nlogn)的方法。所以我们只能优化枚举j的过程。这里要求是 O ( 1 ) O(1) O(1)的复杂度,那么说明j的数目一定是一个小的常数。下面去找到这个常数:
❶如果分成了奇数个序列,总和为 S S S,数量为 m m m,那么值为 S / m S/m S/m,我们将最后一个序列分离(一定递增),和为 T T T。我们将整个序列分为了 ( S − T ) / ( m − 1 ) (S-T)/(m-1) (S−T)/(m−1)和 T T T的序列。而 T − S / m = ( T m − S ) / m T-S/m=(Tm-S)/m T−S/m=(Tm−S)/m, S / m − ( S − T ) / ( m − 1 ) = ( m T − S ) / m ( m − 1 ) S/m-(S-T)/(m-1)=(mT-S)/m(m-1) S/m−(S−T)/(m−1)=(mT−S)/m(m−1),一定是一正一负的,所以一定存在一个序列数更少的情况,答案比原来要大。
❷如果分成了偶数个序列,总和为S,数量为m,那么值为S/m,我们将最后两个序列分离
(第一个序列一定递增),和为T。我们将整个序列分为了(S-T)/(m-2)和T/2的序列。而T/2-S/m=(Tm-2S)/2m,S/m-(S-T)/(m-2)=(mT-2S)/m(m-2),一定是一正一负的,所以一定存在一个序列数更少的情况,答案比原来要大。
❸如此递归下去,我们会发现一个惊人的结论,那就是序列的个数<=2。
如此一来时间复杂度就变成了O(nlogn),解题完毕。
正解程序:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
#define inf 1e15using namespace std;
typedef long long ll;
const ll maxn=100010;
ll n,a[maxn],temp[maxn];
ll dp[maxn][4];
ll f[maxn][4];
ll tree[4*maxn];
map<ll,ll> mp;void change(ll pos,ll l,ll r,ll pla,ll val)
{if(l==r){tree[pos]=val;return;}ll mid=(l+r)>>1;if(pla<=mid)change(pos<<1,l,mid,pla,val);elsechange(pos<<1|1,mid+1,r,pla,val);tree[pos]=max(tree[pos<<1],tree[pos<<1|1]);
}
ll getans(ll pos,ll l,ll r,ll s,ll e)
{if(s<=l && r<=e)return tree[pos];ll mid=(l+r)>>1;ll t1=0,t2=0;if(s<=mid)t1=getans(pos<<1,l,mid,s,e);if(e>mid)t2=getans(pos<<1|1,mid+1,r,s,e);return max(t1,t2);
}
int main()
{memset(dp,0x80,sizeof(dp));memset(f,0x80,sizeof(f));scanf("%lld",&n);for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);temp[i]=a[i];}temp[0]=0;sort(temp,temp+1+n);ll cnt=unique(temp,temp+1+n)-temp-1;for(ll i=0;i<=cnt;i++)mp[temp[i]]=i+1;dp[0][1]=0;f[0][1]=0;double ans=-inf;cnt++;for(ll j=1;j<=2;j++){memset(tree,0x80,sizeof(tree));change(1,1,cnt,1,0);for(ll i=1;i<=n;i++){f[i][j]=f[i-1][j];dp[i][j]=max(dp[i][j],f[i-1][j-1]+a[i]);if(j%2==1){ll value=getans(1,1,cnt,1,mp[a[i]]-1);dp[i][j]=max(dp[i][j],value+a[i]);}else{ll value=getans(1,1,cnt,mp[a[i]]+1,cnt);dp[i][j]=max(dp[i][j],value+a[i]);}change(1,1,cnt,mp[a[i]],dp[i][j]);f[i][j]=max(f[i][j],dp[i][j]);ans=max(ans,dp[i][j]/(double)j);}}printf("%.3lf",ans);return 0;
}
小P的单调区间——解题报告相关推荐
- POJ 2823-Sliding Window单调队列解题报告
链接:http://poj.org/problem?id=2823 利用单调队列的出队入队,维护区间的最值,保证队列单调递增或单调递减,要维护单调递增队列,当一个数字插入的时候,从队尾往前找到第一个比 ...
- 牛客网 小睿睿的方案 解题报告
小睿睿的方案 链接: https://ac.nowcoder.com/acm/contest/371/C 来源:牛客网 题目描述 小睿睿虽然已经是人生赢家了,但当他看见学校里其他人秀恩爱时仍旧会十分不 ...
- openoj的一个小比赛(J题解题报告)poj1703(并查集)
http://openoj.awaysoft.com:8080/judge/contest/view.action?cid=47#problem/J http://poj.org/problem?id ...
- openoj的一个小比赛(F题解题报告)poj3978(dp+素数筛选)
http://openoj.awaysoft.com:8080/judge/contest/view.action?cid=47#problem/F 一个素数帅选法的题目,才开始直接就套模板结构tle ...
- 解题报告 (十) 单调栈
文章目录 单调栈 解题报告 PKU 2082 Terrible Sets HDU 2430 Beans HDU 4252 A Famous City PKU 2796 Feel Good HDU 34 ...
- P2709 小B的询问 解题报告
P2709 小B的询问 解题报告 题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L-R],求 ∑ c i 2 \sum c_i^2 ∑ci2的值,其中i ...
- 【解题报告】2021牛客寒假算法基础集训营4
[解题报告]2021牛客寒假算法基础集训营4 前面的话 A :九峰与签到题 | 模拟 (签到题) B: 武辰延的字符串 | exKMP D :温澈滢的狗狗 | 二分 E: 九峰与子序列 | d p d ...
- 数据结构荣誉课-第一次实验-解题报告
JLU-数据结构荣誉课-第一次实验-解题报告 一.重复计数 题目 思路 参考代码 二.报数游戏 题目 思路 参考代码 三.算术表达式计算 题目 思路 参考代码 四.最喜爱的序列 题目 思路 单调队列 ...
- 【洛谷新手村解题报告二】C++语言,一题多解,思路和WA反思
[洛谷新手村解题报告二] 循环!循环!循环! 数组 继续上次的一!开始循环第二题 循环!循环!循环! 第二题 级数求和 [1/2] 已知:Sn= 1+1/2+1/3+-+1/n 显然对于任意一个整数 ...
最新文章
- 全球及中国抗甲状腺药物行业应用现状调研及未来产销需求预测报告2021-2027年
- Java学习进阶—高级编程
- 开机自启动脚本_使用xtu降低笔记本(游戏本)cpu电压及功耗·游戏本延时(以及试着解决开机自启动的问题)...
- Asp.Net 4.0 SEO增强之UrlRouting
- SMTP、POP3、IMAP的区别
- chrome本地文件加载跨域请求
- Seek the Name, Seek the Fame - POJ 2752(next运用)
- 2017-2018-1 20155315 《信息安全系统设计基础》实验五 通讯协议设计
- webpack(三)使用 babel-loader 转换 ES6代码
- 一例智能网卡(mellanox)的网卡故障分析
- MC9S12 RTI 时钟的设置
- xingtai -斗罗大陆图片下载
- svg图片如何引入vue
- 计算机地图制图相关论文,计算机地图制图原理、特点及发展趋势论文
- 长沙公积金和商贷组合贷款
- 苹果Mac笔记本电脑如何开启热点分享网络?
- python实现北京租房信息计算
- WPF ListBox等ItemsControl交替(Alternation)变化效果
- Java数据对象(JDO)快速入门
- 一开机右下角显示无法连接到服务器,win7右下角提示此连接受限制或无连接怎么办...
热门文章
- 网上报名上传电子照片时,要求大于30K小于200K,该怎么修改?...
- 亚马逊服务器配置php,在亚马逊AWS上建立Git服务器
- 关于 打印机可打印无法扫描报错“使用该设备需要WIA驱动程序,请从安装CS或从...” 的解决方法
- 基于椭球 磁补偿 matlab,基于椭球拟合的三轴磁传感器误差补偿方法.pdf
- 计算机毕业设计Javahtml5大众汽车网站(源码+系统+mysql数据库+lw文档)
- 完善C#汉字转拼音首字母代码
- 清橙1485 Catch The Penguins 抓企鹅
- “火星自治”,马斯克这次是认真的吗?
- 【笔记:JavaWeb】
- 《从0到1-全面深刻理解MySQL系列》- 最详细的MySQL安装流程(Window版)