题目链接

https://ac.nowcoder.com/acm/contest/7604/B

解题思路

大致思路:
首先明白往回跳是没有意义的。
因为本题规定在某一位置能跳到的是一段连续的区间,可能存在往回跳的那个题,题目要求是从某个位置只能跳到某距离远的位置,对应的落脚点是零散的点,要与本题区别开来。正因为我们能跳一个区间的所有位置,所以我们根本不用担心一直往前跳能不能到达终点,它是必然可以啊。
大致贪心思路就有了,在不使用法术的前提下,尽可能往前跳,跳的越远越好。
完了,到了某点了,从前面任何一个位置以任何一种方式都没法跳到这个位置了,完了完了!唉,看来我们必须要使用魔法了。
图片说明
假设不使用魔法能跳到的最远位置是绿色填充的位置,自然,刚好无法到达的位置就是红色填充的位置。
既然红色位置之前的位置都没法到达此位置,使用魔法以到达红色位置已经成为不可改变的事实,但是输出又要求使用魔法的位置要尽量小。那我们就得找最靠左且最远能达到绿色位置的位置吧,假设这个点为i,我们就要从i位置施展法术,让自己能跳的远一个位置,就能达到红色位置了,而且i位置还满足题目要求尽可能小。
整理一下思路,还是上面所说的,尽可能往前跳,当跳到前面的任何位置都无法到达的位置(即红色位置)的时候要在最靠左能到达最远能达到的位置(即绿色位置)的位置(即i位置)施展法术以到达刚好无法到达的位置(即红色位置)


思路是讲完了,还有很多思路上的细节,需要大家自己理解喽!


代码实现上还是有些小细节的,其实我一开始看大佬题解和代码的时候根本没明白咋回事,为什么设这么多变量,感觉只要往前跳就行了。但是自己想了遍过程,理解了如何贪心之后,,代码写起来真是得心应手啊!所以,还是建议大家先理解了这个贪心的过程自己实现一下代码。
具体的代码讲解在代码中注释。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,num,minpos=1,maxdis=1,a[N],ans[N];
/*
num表示施展法术的数目;
minpos表示(两段施展法术之间区间的位置中)离最远能达到点最远的位置,即上述i位置;
maxdis表示(施展下一次法术之前)能达到的最远距离,即上述绿色位置;
ans[i]表示第i次施展法术的位置。
*/
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        if(a[i]){
            if(i+a[i]>maxdis){//如果能跳到的最远距离比记录的maxdis大
                maxdis=i+a[i];//刷新
                minpos=i;//记录下来能跳到最远距离对应的第一个位置,就是最靠左的位置
            }
        }
        else if(maxdis==i){//前面区间的位置最远也就到i(此i非上述i,而是循环遍历到的i)了,必须施展法术了
            ans[++num]=minpos;//把记录的最靠前的能一次到达最远距离的位置,加入答案中
            maxdis=i+1;//更新最远能到达的位置为i+1
            minpos=i+1;//更新最靠前能到达maxdis的位置为i+1
            //上面两步可以理解为“又回到最初的起点,记忆中你青涩的脸……”,就相当于又开辟了一段区间,而这段区间的左端点正是你上次施展法术后刚好到达的位置。略讲在另附中。
        }
        if(maxdis>n) break;
    }
    cout<<num<<endl;
    for(int i=1;i<=num;i++) cout<<ans[i]<<' ';
} 

另附

图片说明
红色代表刚好跳不到的位置,绿色代表可以跳达的位置。(挺抽象的不好解释)
绿1区间的所有位置最远能一步跳到红1位置的前一个位置;
绿2区间的所有位置最远能一步跳到红2位置的前一个位置;
……
绿4区间可以内任何一点都可以通过某种向前跳(可以多次)的跳法跳到n+1位置。
其实实现了区间化。