题解 P1725 琪露诺
Posted luckyblock
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1725 琪露诺相关的知识,希望对你有一定的参考价值。
可以非常简单地推出转移方程式:
f[i]= max(f[i-r,i-r])+a[i]
然后 不用脑子地 用常规思路敲出来:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MARX=0xf;
int n,l,r,ans;
int a[300010];
int f[300010];
int main()
memset(f,-MARX,sizeof(f));//初始化极小值
ans=-2147483640;
f[0]=0;
scanf("%d%d%d",&n,&l,&r);
for(int i=0;i<=n;i++)
scanf("%d",&a[i]);
//===========输入与处理的境界============================
for(int i=1;i<=n;i++)
for(int j=i-r;j<=i-l;j++)//枚举能到达i点的每一个点
if(j>=0)//不出界
f[i]=max(f[i],f[j]+a[i]);
for(int i=n-r+1;i<=n;i++)//找到能到达对岸的点
ans=max(ans,f[i]);
printf("%d",ans);
时间复杂度为O(n*(r-l))
很遗憾,只有60分,TLE了4个点。
再 装上脑子 回头看该题的数据范围:
N<=200,000 且 1<= L<=R<=N
显然,上面O(n*(r-l))的时间复杂度, 会超到爆, 只有⑨才会用
仔细分析题目,可以发现:
对于第i个点与第i+1个点,
能到达第i个点的点在区间[ i-R , i-L ]中
能到达第i+1个点的点在区间[ i-R+1 , i-L+1 ]中
对于区间 [ i-R+1 , i-L+1 ],可以由区间[ i-R , i-L ]整体向右移动一位来得到.
这不禁让我们想到了另一道题目:P1886 滑动窗口
如果没有学习过单调队列,
推荐这篇文章:朝花中学OI队的奋斗历程——浅谈单调队列
学习一下单调队列。单调队列真是个好东西
可以发现,本题是一道 用单调队列维护的DP
- 对于每一个点i,
- 每次都将i-L放入单调队列,
- 并以 f[x] 为标准,对单调队列进行维护
PS:
//就相当于⑨做法的这里
for(int j=i-r;j<=i-l;j++) //枚举能到达i点的每一个点
if(j>=0)
f[i]=max(f[i],f[j]+a[i]);
- 然后将队首,到达不了第i个点的,即 x+R<i 的,删去
- 找到第一个能到达第i个点的,即在区间[ i-R , i-L ]中,f[x]最大的点。
- 并用此点的f[x]来更新f[i]即可
- 全扫一遍后,再从能够到达河对岸的点
- 即属于[n-r+1,n]的点中,找最大的f[x]
- 得到的就是答案
附上AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MARX=0xf;
int n,l,r,ans;
int a[300010];
int f[300010];
int queue[300010];
int head[300010];
int h=1,t=1;
int maxx(int i)//获取最大的f[i],单调队列模板
while(queue[t]<=f[i] && t>=h)
t--;
queue[++t]=f[i];//放入
head[t]=i;//记录序号
while(queue[h]+r<i)//删掉不能到达的
h++;
return head[h];//返回
int main()
memset(f,-MARX,sizeof(f));//初始化极小值
ans=-2147483640;
f[0]=0;
scanf("%d%d%d",&n,&l,&r);
for(int i=0;i<=n;i++)
scanf("%d",&a[i]);
for(int i=l;i<=n;i++)//DP过程
int k=maxx(i-l);
f[i]=f[k]+a[i];
if(i>=n-r+1)//找到能到达河对岸的,即答案
ans=max(ans,f[i]);
printf("%d",ans);
当然,也可以用优先队列,来实现单调队列:
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int MARX=0xf;
int n,l,r,ans;
int a[300010];
int f[300010];
int h=1,t=1;
struct cmp1//自定义优先级,以冰冻指数和降序排列
bool operator ()(const int a,const int b)
return f[a]<f[b];
;
priority_queue < int,vector<int>,cmp1 > q;
int maxx(int i)//获取最大的f[x]
q.push(i);
while(q.top()+r<i)
q.pop();
return q.top();
int main()
memset(f,-MARX,sizeof(f));//初始化极小值
ans=-2147483640;
f[0]=0;
scanf("%d%d%d",&n,&l,&r);
for(int i=0;i<=n;i++)
scanf("%d",&a[i]);
for(int i=l;i<=n;i++)//DP过程
int k=maxx(i-l);
f[i]=f[k]+a[i];
if(i>=n-r+1)//找到能到达河对岸的,即答案
ans=max(ans,f[i]);
printf("%d",ans);
完成了这篇题解,车万厨信仰++
以上是关于题解 P1725 琪露诺的主要内容,如果未能解决你的问题,请参考以下文章