Skipping题解(贪心,队列,迪杰斯特拉,思维)
-
大致题意
类似一个游戏游戏规则如下:- 从1开始,可以选择吃掉ai,之后只能选择比当前下标小的(明显,如果开始就选择吃掉a1,那么游戏就结束了)。
- 当然还可以选择不吃ai,那么可以跳到[1,b[i]](b[i]与i的大小关系并不确定)。
我们要吃的尽可能多,求最多可以吃多少。
-
上思路
这个题的第一个思维特征——贪心, 拿第2个规则来说,可以跳的范围是[1, b[i]], 但是我们应该要跳到b[i]且b[i]>i。 试想如果b[i]<=i,我们大可使用规则1,吃掉ai后,再选择i-1,之后一步一步吃掉编号小的且之前没有跳过的;所以这时用规则1会更划算。
我们可以选择+ai跳到i-1,当b[i]>i时,可以考虑+0跳到b[i];
理解了这个小tip以后,可以真正来看关键代码的实现步骤了。
本人很喜欢用队列。
关于这个题,可以说是最短路的思想,也就是优先队列弹出最短的那条边, 但是我稍稍优化了一下。
我们这样考虑, 从1开始,我们负债为0,若b[1]>1,我们跳到b[1]就需要花费a[i],用dis[i]表示从1不断跳跃到达i时需要的最小花费(应该是个负数),sum数组是a[i]的前缀和,sum[i]+dis[i]就表示到达i点时能吃掉的最大量。
当然,有些点可能到不了,所以dis[]初始化为-2e18就可以了。
优先队列维护的是当前dis[i]最大的(和最短路差不多), 遍历max(t.x, 上一次跳跃的)到b[t.x]的,若这些有可以跳的更远的,就加入队列。
-
上代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int xmmm=4e5+10;
struct p{
int x, y;
friend bool operator < (p f1, p f2){
return (f1.y<f2.y)||(f1.y==f2.y&&f1.x<f2.x);
}
};
int a[xmmm], b[xmmm], sum[xmmm];
int dis[xmmm];
void bfs(){
int l=1, r=0;
priority_queue<p>q;
q.push(p{(int) 1, dis[1]});
while(!q.empty()){
p t=q.top();q.pop();
if(t.x<=r)continue;
l=r, r=t.x;
for(int i=l+1;i<=r;i++){
if(dis[i]<=dis[t.x]){
dis[i]=dis[t.x];
if(b[i]>r){
if(dis[b[i]]<dis[i]-a[i]){
dis[b[i]]=dis[i]-a[i];
q.push(p{b[i], dis[b[i]]});
}
}
}
}
}
return ;
}
signed main()
{
int T;cin>>T;
while(T--){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];dis[i]=-2e18;
}
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
for(int i=1;i<=n;i++)cin>>b[i];
dis[(int)1]=0;
bfs();
int ans=0;
for(int i=n;i>=1;i--){
ans=max(ans, dis[i]+sum[i]);
}
cout<<ans<<'\n';
}
return 0;
}
-
自我总结
这个题比较有意思的点就是它暗含了一个迪杰斯特拉的模型,而对这个模型的使用就看自己的理解了。
第36次ccf csp认证的D题和这个题思考方式相似。
写了挺久的。