2020牛客NOIP赛前集训营-普及组(第一场)B-牛牛的跳跳棋

题目

在这里插入图片描述
在这里插入图片描述


对于某个原先跳不到的点i,我们选择一个序号最小的点使得这个点可以跳到i-1,然后延伸它,同时记录答案。
注意延伸完一次后应从当前点出发重新延伸。


代码

#include<cstdio>  
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,d[100005],s[100005],jump[100005],b[100005],ans[100005],t;
int main(){
	scanf("%d",&n);
	memset(d,0x7f,sizeof(d));
	d[1] = 0; s[1] = 1;
	for(int i = 1; i <= n; ++i){
		scanf("%d", &jump[i]);
		if(jump[i] > 0){  //可以跳
			++ b[i + 1];  //用差分处理可以跳的格子
			-- b[min(n + 2, i + jump[i] + 1)];
		}
		d[min(n + 1, i + jump[i])] = min(i, d[i + jump[i]]); 
		//对于最大可以跳到的那个格子,可以最小从哪个点跳到。【即用于处理答案最小序】
		s[i] = s[i-1] + b[i];  //差分
		if(s[i] == 0){    //如果这个点不能从前面某个点调过来
			ans[++t] = d[i-1];  //从能直接跳到第i-1个点的点延伸一步跳过来
			d[i] = i;  //不能连续跳了,所以如果下一点需要延伸,只能从当前点跳过去
		}
	}
	s[n+1] = s[n] + b[n+1];  //最后一步
	if(s[n + 1] == 0)
			ans[++t] = d[n];
	printf("%d\n",t-1);
	for(int i = 2; i <= t; ++i)
	  printf("%d ", ans[i]);
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页