小溪里有 n 块石头排成一行,从左起编号为 1、2、…、n。 第 i 块石头上写着整数 a_i。 坐在第 i 块石头上的青蛙总是跳到第 i + a_i 块石头。 a_i 为负数时向左跳,为 0 时原地跳。 如果落点小于 1 或大于 n,青蛙就离开了小溪。 如果青蛙落到它已经坐过的石头上,就会无休止地重复同一条路,永远离不开小溪。 出发的石头也算坐过的石头。 q 只青蛙依次各自出发。 前一只青蛙的路线与后一只无关。 第一行给出 n,第二行给出 a_1 到 a_n,第三行给出 q,第四行给出每只青蛙出发的石头编号,用空格隔开。 每只青蛙输出一行:如果离开小溪,输出离开前跳的次数,跳出去的最后一跳也算。 如果离不开,输出 -1。
输入
1 ≤ n ≤ 2,000,-n ≤ a_i ≤ n,1 ≤ q ≤ 200,1 ≤ 出发石头编号 ≤ n
输出
q 行,每行为跳的次数或 -1
样例 1 · stdin
6
2 4 -2 -4 -3 1
5
1 2 4 5 6
stdout
-1
2
1
3
1
main.py
错误列表00F8 ↓ · ⇧F8 ↑
输入(stdin)
运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。
$ 就绪
请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。
其他语言怎么写?查看各语言题解 · 8 种语言题解
石头编号从 1 开始,而八种语言的列表下标都从 0 开始,所以出发时用 i = s - 1 换算一次,之后只按下标思考最不容易乱。 这样"在小溪里"就是 0 <= i < n(Python 可以直接写 0 <= i < n,其他语言写 i >= 0 && i < n)。 不换算而从 a[s] 开始,就会从右边一块石头出发;把边界检查写成 i < n - 1,就会把第 n 块石头当成溪外。 要先检查范围再读 a[i]。 越界读取时,Python 会把负下标从后往前数而悄悄出错(a[-1] 是最后一块石头),JS/TS 给出 undefined,i + undefined 变成 NaN。 Java、C# 抛异常,Craykon 以运行错误停止,C/C++ 数组会读到任意值,是未定义行为。 坐过的石头记录必须每只青蛙重新建:Python [False] * n,JS new Array(n).fill(false),Java new boolean[n],C# new bool[n](两者一开始都是 false),C memset(seen, 0, sizeof seen),C++ std::vector<bool>(n, false),Craykon 新列表(n, 假)。 如果所有青蛙共用一份记录,青蛙只是落到前一只踩过的石头上,也会被判为困住。 漏数跳出去的最后一跳也是常见错误。
Pythonmain.py · 14 行
n = int(input())a = list(map(int, input().split()))q = int(input())out = []for s in map(int, input().split()): seen = [False] * n # 개구리마다 새로 — 앞 개구리의 기록을 쓰면 안 된다 i = s - 1 # 돌 번호(1부터) → 목록 번호(0부터) jumps = 0 while 0 <= i < n and not seen[i]: seen[i] = True i += a[i] jumps += 1 out.append(jumps if not 0 <= i < n else -1)print("\n".join(map(str, out)))
JavaScriptmain.js · 19 行
const tokens = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let pos = 0;const n = tokens[pos++];const a = tokens.slice(pos, pos + n);pos += n;const q = tokens[pos++];const out = [];for (let t = 0; t < q; t++) { const seen = new Array(n).fill(false); // 개구리마다 새로 let i = tokens[pos++] - 1; // 돌 번호(1부터) → 배열 번호(0부터) let jumps = 0; while (i >= 0 && i < n && !seen[i]) { seen[i] = true; i += a[i]; jumps++; } out.push(i >= 0 && i < n ? -1 : jumps);}console.log(out.join("\n"));
TypeScriptmain.ts · 20 行
import { readFileSync } from "node:fs";const tokens: number[] = readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let pos = 0;const n = tokens[pos++];const a: number[] = tokens.slice(pos, pos + n);pos += n;const q = tokens[pos++];const out: number[] = [];for (let t = 0; t < q; t++) { const seen: boolean[] = new Array<boolean>(n).fill(false); // 개구리마다 새로 let i = tokens[pos++] - 1; // 돌 번호(1부터) → 배열 번호(0부터) let jumps = 0; while (i >= 0 && i < n && !seen[i]) { seen[i] = true; i += a[i]; jumps++; } out.push(i >= 0 && i < n ? -1 : jumps);}console.log(out.join("\n"));
JavaMain.java · 28 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer;public class Main { public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine().trim()); int[] a = new int[n]; StringTokenizer st = new StringTokenizer(in.readLine()); for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken()); int q = Integer.parseInt(in.readLine().trim()); st = new StringTokenizer(in.readLine()); StringBuilder out = new StringBuilder(); for (int t = 0; t < q; t++) { boolean[] seen = new boolean[n]; // 개구리마다 새로(모두 false) int i = Integer.parseInt(st.nextToken()) - 1; // 돌 번호(1부터) → 배열 번호(0부터) int jumps = 0; while (i >= 0 && i < n && !seen[i]) { seen[i] = true; i += a[i]; jumps++; } out.append(i >= 0 && i < n ? -1 : jumps).append('\n'); } System.out.print(out); }}
C#Main.cs · 29 行
using System;using System.Linq;using System.Text;class Program{ static void Main() { int n = int.Parse(Console.ReadLine()); int[] a = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); Console.ReadLine(); int[] starts = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToArray(); var sb = new StringBuilder(); foreach (int s in starts) { bool[] seen = new bool[n]; // 개구리마다 새로(모두 false) int i = s - 1; // 돌 번호(1부터) → 배열 번호(0부터) int jumps = 0; while (i >= 0 && i < n && !seen[i]) { seen[i] = true; i += a[i]; jumps++; } sb.Append(i >= 0 && i < n ? -1 : jumps).Append('\n'); } Console.Write(sb); }}
C++main.cpp · 24 行
#include <iostream>#include <vector>int main() { std::ios::sync_with_stdio(false); int n, q; std::cin >> n; std::vector<int> a(n); for (int& x : a) std::cin >> x; std::cin >> q; for (int t = 0; t < q; t++) { int s; std::cin >> s; std::vector<bool> seen(n, false); // 개구리마다 새로 int i = s - 1; // 돌 번호(1부터) → 배열 번호(0부터) int jumps = 0; while (i >= 0 && i < n && !seen[i]) { seen[i] = true; i += a[i]; jumps++; } std::cout << ((i >= 0 && i < n) ? -1 : jumps) << '\n'; }}
Cmain.c · 26 行
#include <stdio.h>#include <string.h>int main(void) { static int a[2000]; static char seen[2000]; int n, q; scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &a[i]); scanf("%d", &q); for (int t = 0; t < q; t++) { int s; scanf("%d", &s); memset(seen, 0, sizeof seen); /* 개구리마다 새로 */ int i = s - 1; /* 돌 번호(1부터) → 배열 번호(0부터) */ int jumps = 0; /* 범위를 먼저 물어야 a[i]·seen[i]가 배열 밖을 읽지 않는다 */ while (i >= 0 && i < n && !seen[i]) { seen[i] = 1; i += a[i]; jumps++; } printf("%d\n", (i >= 0 && i < n) ? -1 : jumps); } return 0;}
Craykonmain.cray · 25 行
n = 控制台.读行().转数字();a = 控制台.读数字();q = 控制台.读行().转数字();starts = 控制台.读数字();遍历 (s 在 starts){ seen = 新列表(n, 假); // 개구리마다 새로 i = s - 1; // 돌 번호(1부터) → 목록 번호(0부터) jumps = 0; // 범위를 먼저 물어야 seen[i]가 목록 밖을 읽지 않는다 当 (i >= 0 && i < n && !seen[i]) { seen[i] = 真; i += a[i]; jumps += 1; } 如果 (i >= 0 && i < n) { 控制台.输出(-1); } 否则 { 控制台.输出(jumps); }}