【2020.10.17 牛客 普及组 模拟赛一】T2 牛牛的跳跳棋

102 篇文章 1 订阅
16 篇文章 0 订阅

题目描述
牛牛最近在玩一种叫做跳跳棋的游戏,棋盘可以看成是一个一维的线性数组,编号从1到 n + 1 n+1 n+1
一开始牛牛的棋子位于第1个格子,游戏的最终目的是将棋子移动到第 n + 1 n+1 n+1个格子。
棋盘1~n的每个格子都有一个“弹力系数”的权值 p i ​ p_i​ pi
当棋子位于第i个格子时,它的下一步可以移动到 [ i − p i , i + p i ] [i-p_i,i+p_i] [ipi,i+pi]范围内的任意一个格子。
举例来说,假设第3个格子的弹力系数为2,那么牛牛下一步可以移动到第1,2,3,4,5格中的任意一格。
现在给定 1   n 1~n 1 n每格的弹力系数 p i p_i pi。牛牛发现,好像有时由于棋盘的 p i p_i pi设置不合理,导致游戏无法通关。
所以牛牛准备施展他神奇的魔法,他每次施展魔法都可以使得一个格子的弹力系数 p i + 1 p_i+1 pi+1,他可以施展若干次魔法操作不同的格子,但是要求他不能够重复对一个格子施展魔法。

牛牛想要知道,为了使跳跳棋通关,他最少施展多少次魔法,并且他应该操作哪些格子。
请输出牛牛的最小操作次数,以及施展魔法的操作序列,操作序列的第i个数表示该次施展魔法的格子编号,由于答案不唯一,所以请你输出一个最小字典序的答案。

最小字典序指:在保证第1个数字尽可能小的前提下,保证第2个数字尽可能的小,然后在此前提下保证第3个数字尽可能的小…以此类推。


输入描述:
第一行输入一个正整数n表示跳跳棋的格子数目。
接下来输入一行n个非负整数 p i p_i pi
表示跳跳棋前n个格子的弹力系数。

输出描述:
首先输出一个非负整数ans,表示少施展魔法的次数。
如果ans不为0,则再输出一行ans个整数表示需要施展魔法的格子编号,请给出一个最小字典序的答案。


示例1
输入
12
5 4 3 3 2 1 0 0 0 1 0 0

输出
5
4 8 9 10 12

说明
除了 " 4891012 " "4 8 9 10 12" "4891012"这个操作的答案序列以外, " 5891012 " , " 6891012 " "5 8 9 10 12","6 8 9 10 12" "5891012","6891012"也同样是最小操作数下的答案。
但是 " 4891012 " "4 8 9 10 12" "4891012"这个答案是字典序最小的,故输出 " 4891012 " "4 8 9 10 12" "4891012"

示例2
输入
8
0 1 0 1 0 1 0 1

输出
4
1 2 4 6

示例3
输入
5
0 0 0 0 0

输出
5
1 2 3 4 5

说明
本样例可以说明,不存在无解的情况,因为你至少可以令所有 p i p_i pi 全都+1。

示例4
输入
5
1 1 1 1 1

输出
0


备注:
对于 20 20 20%的测试数据,保证 1 ≤ n ≤ 10 1≤n≤10 1n10
对于 40 40 40%的测试数据,保证 10 ≤ p i ≤ 1 10≤p_i ≤1 10pi1
对于 100 100 100%的测试数据,保证 1 ≤ n ≤ 1 0 5 , 0 ≤ p i ≤ 100 1≤n≤10^5 ,0≤p_i≤100 1n105,0pi100


解题思路
这道题是一道贪心。

我们可以看出,往后走是没有任何的必要的。
因为你走是可以走到 [ i − p i , i + p i ] [i-p_i,i+p_i] [ipi,i+pi]中的任意一个点,那你走回去,再走回来,其实不如你直接往前走。

那这道题就可以很愉快的用贪心解决了,在用魔法之前竟可能的走到更前面,然后在跳最后一次的地方用一次魔法。

这时候可能会有人问:为什么这样一定可以呢?
因为它无论在什么地方使用魔法,都只能在原来的基础上多走一步,那肯定就是先找到能走到最远地方的最后一点,然后再那个地方用魔法。

然后我们只要在走的时候记录一下在那些地方用了魔法,在到终点之后输出出来就可以了。

题目要求要字典序最小,那我们不往后走,而且如果有两个地方都可以跳到最远点,就选前面的那个。


代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
int n,a[110000],num[110000],maxn,maxx;
void output(){
	printf("%d\n",num[0]);
	for(int i=1;i<=num[0];i++)
		printf("%d ",num[i]);
}
int main(){
	scanf("%d",&n);
	maxn=1,maxx=1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]) 	
		{
			if(a[i]+i>maxn)
			{
				maxn=a[i]+i;
				maxx=i;
				if(maxn>n)
				{
					output();
					return 0;
				}
			}
			
		}
		else if(i==maxn) 
		{
			num[++num[0]]=maxx;
			maxn++;
			maxx=maxn;
			if(maxn>n)
				output();	
		}
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值