版权声明:未经本蒟蒻同意,请勿转载本蒻博客 https://blog.csdn.net/wddwjlss/article/details/83215646
一、琪露诺:
题意:一开始在
号格子上,每个格子有一个权值,在格子
时,下一次可以移动到区间
中的任意一格,只要下一步的位置编号大于
就算到达对岸,求最大权值。
首先如果不看数据范围,这是一个普通的dp,设 表示到达 这个点的最大权值,得转移方程:
复杂度
考虑如何优化,我们发现这个东西类似于滑动窗口,每次的区间是固定的。我们维护一个单调递减的队列,每到一个格子 先将队列中编号小于 的元素出列(从队首开始做),因为这些元素不能用于更新后面的答案了,然后将队尾小于等于 的元素出列,最后将 压入队尾,则 。最后答案就是 。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,l,r,val[1001000],ans;
int f[1001000];
struct node
{
int val,pos;
}q[1001000];
int main()
{
//freopen("testdata.in","r",stdin);
cin>>n>>l>>r;
for(int i=0;i<=n;++i)
scanf("%d",&val[i]);
int head=1,tail=0;
for(int i=l;i<=n;++i)
{
while(head<=tail&&f[i-l]>=q[tail].val)
tail--;
tail++;
q[tail].val=f[i-l];
q[tail].pos=i-l;
while(i-q[head].pos>r-l)
head++;
f[i]=q[head].val+val[i];
}
for(int i=n-r+1;i<=n;++i)
ans=max(ans,f[i]);
printf("%d",ans);
return 0;
}
二、跳房子:
题意:
个格子,每个格子距离起点有距离,每个格子有得分。每次可以向右跳
个距离,但可以花费
使得每次可以向右跳
(如果
则为
)个距离。只要跳到这个格子即会获得这个格子的得分,问至少得
分的情况下的最少花费是多少。
首先
是可以二分的,对于每一个二分到的
,我们需要用一个dp来检验最终得分是否大于
,我们设
表示跳到第
个格子所能获得的最大分数,转移方程为:
看到这个式子,发现是典型的单调队列优化dp,这里单调队列维护的是格子的编号。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll l,r=1e7,ans,d,k;
int n;
ll x[1001000],s[1001000],dp[1001000],q[1001000];
bool check(int mid)
{
for(int i=1;i<=n;++i)
dp[i]=-1e9;
memset(q,0,sizeof(q));
dp[0]=0;
ll maxx,minn;
if(mid>=d)
minn=1;
else
minn=d-mid;
maxx=d+mid;
int now=0;//now为未处理完的格子
int head=1,tail=0;
for(int i=1;i<=n;i++)
{
while(x[i]-x[now]>=minn&&i>now)
//把距离当前格minn内的未添加到单调队列的格子添加到单调队列
{
if(dp[now]!=-1e9)
{
while(head<=tail&&dp[q[tail]]<=dp[now])
tail--;
tail++;
q[tail]=now;
}
now++;
}
while(head<=tail&&x[i]-x[q[head]]>maxx)
head++;
if(head<=tail)
dp[i]=dp[q[head]]+s[i];
if(dp[i]>=k)
return true;
}
return false;
}
ll sum=0;
int main()
{
cin>>n>>d>>k;
for(int i=1;i<=n;++i)
{
scanf("%lld%lld",&x[i],&s[i]);
if(s[i]>0)
sum+=s[i];
}
if(sum<k)
{
cout<<"-1";
return 0;
}
while(l<=r)
{
ll mid=(l+r)/2;
if(check(mid))
{
r=mid-1;
ans=mid;
}
else
l=mid+1;
}
cout<<ans;
return 0;
}