一、琪露诺:
题意:一开始在
0
0
0号格子上,每个格子有一个权值,在格子
i
i
i时,下一次可以移动到区间
[
i
+
l
,
i
+
r
]
[i+l,i+r]
[i+l,i+r]中的任意一格,只要下一步的位置编号大于
n
n
n就算到达对岸,求最大权值。
(
n
<
=
2
e
5
)
(n<=2e5)
(n<=2e5)
首先如果不看数据范围,这是一个普通的dp,设 f [ i ] f[i] f[i]表示到达 i i i这个点的最大权值,得转移方程: f [ i ] = m a x { f [ i − j ] } + v a l [ i ] , l ≤ j ≤ r ≤ i f[i]=max\lbrace f[i-j] \rbrace +val[i],l≤j≤r≤i f[i]=max{f[i−j]}+val[i],l≤j≤r≤i
复杂度 O ( n 2 ) O(n^2) O(n2)
考虑如何优化,我们发现这个东西类似于滑动窗口,每次的区间是固定的。我们维护一个单调递减的队列,每到一个格子 i i i先将队列中编号小于 i − r i-r i−r的元素出列(从队首开始做),因为这些元素不能用于更新后面的答案了,然后将队尾小于等于 f [ i − l ] f[i-l] f[i−l]的元素出列,最后将 f [ i − l ] f[i-l] f[i−l]压入队尾,则 f [ i ] = a [ i ] + q [ h e a d ] f[i]=a[i]+q[head] f[i]=a[i]+q[head]。最后答案就是 m a x { f [ i ] } , i ∈ [ n − r + 1 , n ] max \lbrace f[i]\rbrace,i∈[n-r+1,n] max{f[i]},i∈[n−r+1,n]。
#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;
}
二、跳房子:
题意:
n
n
n个格子,每个格子距离起点有距离,每个格子有得分。每次可以向右跳
d
d
d个距离,但可以花费
g
g
g使得每次可以向右跳
[
d
−
g
,
d
+
g
]
[d-g,d+g]
[d−g,d+g](如果
d
−
g
<
1
d-g<1
d−g<1则为
[
1
,
d
+
g
]
[1,d+g]
[1,d+g])个距离。只要跳到这个格子即会获得这个格子的得分,问至少得
k
k
k分的情况下的最少花费是多少。
首先
g
g
g是可以二分的,对于每一个二分到的
g
g
g,我们需要用一个dp来检验最终得分是否大于
k
k
k,我们设
d
p
[
i
]
dp[i]
dp[i]表示跳到第
i
i
i个格子所能获得的最大分数,转移方程为:
d
p
[
i
]
=
m
a
x
{
d
p
[
i
−
j
]
}
+
s
[
i
]
,
j
∈
[
d
−
g
,
d
+
g
]
dp[i]=max\lbrace dp[i-j]\rbrace+s[i],j∈[d-g,d+g]
dp[i]=max{dp[i−j]}+s[i],j∈[d−g,d+g]
看到这个式子,发现是典型的单调队列优化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;
}