{ }codelper
声明变量累计P0328

飞镖记分板:每个值的寿命不同

飞镖游戏一共玩 n 局。
一局中掷 k 次飞镖,每次得到 0 到 10 分。
得 0 分的一掷叫作“失误”,一次失误都没有的一局叫作“干净局”。
每一局求三个值。
局得分是这一局各次得分之和。
不过,如果紧挨着的上一局是干净局,就是这个和的两倍。
第一局没有上一局,所以不翻倍。
累计得分是从第一局到这一局的局得分之和。
首次失误位置是这一局中第一次出现 0 分的是第几掷(第一掷是第 1 掷)。
这一局没有失误时为 0。
游戏结束后再求三个值。
局得分最高的那一局的编号(有多局时取最早的一局)、那一局的局得分,以及干净局不间断地连续出现的最多局数(没有干净局时为 0)。
在样例中,第 2 局是干净局,但第 1 局有失误,所以局得分仍是 8。
第 3 局因为第 2 局是干净局,所以是 6 的两倍 12。
第 4 局自己有失误,但第 3 局是干净局,所以是 6 的两倍 12。
局得分 12 出现了两次,所以最高的一局是先玩的第 3 局;干净局是第 2 局和第 3 局相连,共 2 局。

输入
第一行给出 n。
接下来 n 行每行一局,依次给出掷的次数 k 和按掷出顺序排列的 k 个得分,用一个空格分隔。
1 ≤ n ≤ 1,000,1 ≤ k ≤ 20,得分是 0 到 10 的整数
输出
每一局输出一行,依次是局得分、累计得分、首次失误位置,用一个空格分隔。
最后一行输出局得分最高的那一局的编号、那一局的局得分、干净局连续出现的最多局数,用一个空格分隔
样例 1 · stdin
4
3 5 0 2
2 4 4
3 1 2 3
2 0 6
stdout
7 7 2
8 15 0
12 27 0
12 39 1
3 12 2
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 8 种语言题解

这道题里的值有三种寿命。
整场游戏一直延续的值(累计得分、最高的一局、当前连续局数)、每局重新开始的值(局得分、首次失误位置)、只在下一局用一次的值(上一局是否干净)。
变量在哪里声明,就决定了它的寿命。
C、C++、Java、C#、JS、TS(let)中,在循环里声明的变量每局都重新创建,块结束就消失。
陷阱一(遮蔽):在 if 里再次声明同名变量,C、C++、JS、TS(let)会悄悄创建一个遮住外层变量的新变量。
if (prev) { int score = raw * 2; } 改不了外层的 score,样例的第 3 局会得到 6。
Java 会报 variable score is already defined,C# 会报 CS0136,都无法编译,正好挡住这个错误。
JS 的 var 的作用域不是块而是整个函数,所以 if 里的 var v = 6 会改掉外层的 v。
Python 既没有块作用域也没有声明,在 if 里赋的值在外面照样看得到。
陷阱二(循环变量):for (int i …) 的 i 在循环外使用,C、C++、Java、C#、TS 是编译错误,JS 的 let 是 ReferenceError。
Python 在 for i in range(k) 之后 i 还在,但循环跑完时它的值是 k - 1 而不是 k。
所以照 C 的写法用“i == k 就是没找到”来判断,干净局的首次失误位置就不是 0 而是 k(样例的第 2 局得到 2)。
应当在找到的地方直接记下,或者用 for … else。
陷阱三:把每局重新开始的值放在循环外,上一局的值会留下来;把整场延续的值放在循环里,每局都会变回 0。
陷阱四:“上一局是否干净”要在算完这一局的得分之后再改。
先改的话,这一局会因为自己的结果而翻倍。
陷阱五:当前连续局数在有失误的一局要归 0;得分相同时取先玩的一局,所以用 > 而不是 >= 比较。
初始值也不同。
C、C++ 的局部变量不赋值就读取是未定义行为,读出什么值无法预料,Java(might not have been initialized)和 C#(CS0165)则是编译错误。
Craykon 没有单独的声明,变量在第一次赋值的地方产生。
在块里或 循环 (i = 1; …) 里第一次创建的变量,块结束就消失,在外面使用会以“还没有创建的名字”在运行前停下。
所以整场要用的值要在循环之前像 total = 0; 这样先创建。
给已经存在的名字赋值总是改外层的那个变量,所以不会出现遮蔽的错误。

Pythonmain.py · 45 行
import sys def main():    lines = sys.stdin.read().split("\n")    n = int(lines[0])     # 놀이 내내 살아 있는 값    total = 0    best_round = 0    best_score = -1    streak = 0            # 지금 이어지는 깨끗한 판 수    longest = 0    prev_clean = False    # 바로 앞 판이 깨끗했는가 (다음 판에서 한 번 쓰고 바뀐다)     out = []    for r in range(1, n + 1):        throws = [int(v) for v in lines[r].split()][1:]         # 이 판에서만 쓰는 값: 판마다 새로 시작한다        miss = 0        for i, s in enumerate(throws):            if s == 0:                miss = i + 1      # 반복이 끝난 뒤의 i에 기대지 않고, 찾은 그 자리에서 적는다                break        score = sum(throws)        if prev_clean:            score *= 2            # if 안에서 바꿔도 같은 score다 (Python에는 블록 범위가 없다)         total += score        if score > best_score:            best_score = score            best_round = r        clean = miss == 0        if clean:            streak += 1            longest = max(longest, streak)        else:            streak = 0        prev_clean = clean        # 이 판의 결과를 다 쓴 뒤에 넘긴다        out.append(f"{score} {total} {miss}")     out.append(f"{best_round} {best_score} {longest}")    print("\n".join(out)) main()
JavaScriptmain.js · 46 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]); // 놀이 내내 살아 있는 값: 반복문 밖에 let으로let total = 0;let bestRound = 0;let bestScore = -1;let streak = 0; // 지금 이어지는 깨끗한 판 수let longest = 0;let prevClean = false; // 바로 앞 판이 깨끗했는가 const out = [];for (let r = 1; r <= n; r++) {  const throws = lines[r].trim().split(/\s+/).map(Number).slice(1);   // 이 판에서만 쓰는 값: 반복문 안에서 선언하면 판마다 새로 생긴다  let miss = 0;  for (let i = 0; i < throws.length; i++) {    if (throws[i] === 0) {      miss = i + 1; // i는 이 for 안에서만 보이므로, 찾은 그 자리에서 적는다      break;    }  }  let score = 0;  for (const s of throws) score += s;  if (prevClean) {    score *= 2; // 여기서 let score를 또 쓰면 바깥 score와 다른 새 변수가 된다  }   total += score;  if (score > bestScore) {    bestScore = score;    bestRound = r;  }  const clean = miss === 0;  if (clean) {    streak++;    if (streak > longest) longest = streak;  } else {    streak = 0;  }  prevClean = clean; // 이 판의 결과를 다 쓴 뒤에 넘긴다  out.push(`${score} ${total} ${miss}`);}out.push(`${bestRound} ${bestScore} ${longest}`);console.log(out.join("\n"));
TypeScriptmain.ts · 47 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]); // 놀이 내내 살아 있는 값: 반복문 밖에 let으로let total = 0;let bestRound = 0;let bestScore = -1;let streak = 0; // 지금 이어지는 깨끗한 판 수let longest = 0;let prevClean = false; // 바로 앞 판이 깨끗했는가 const out: string[] = [];for (let r = 1; r <= n; r++) {  const throws: number[] = lines[r].trim().split(/\s+/).map(Number).slice(1);   // 이 판에서만 쓰는 값: 반복문 안에서 선언하면 판마다 새로 생긴다  let miss = 0;  for (let i = 0; i < throws.length; i++) {    if (throws[i] === 0) {      miss = i + 1; // i는 이 for 안에서만 보이므로, 찾은 그 자리에서 적는다      break;    }  }  let score = 0;  for (const s of throws) score += s;  if (prevClean) {    score *= 2; // 여기서 let score를 또 쓰면 바깥 score와 다른 새 변수가 된다  }   total += score;  if (score > bestScore) {    bestScore = score;    bestRound = r;  }  const clean: boolean = miss === 0;  if (clean) {    streak++;    if (streak > longest) longest = streak;  } else {    streak = 0;  }  prevClean = clean; // 이 판의 결과를 다 쓴 뒤에 넘긴다  out.push(`${score} ${total} ${miss}`);}out.push(`${bestRound} ${bestScore} ${longest}`);console.log(out.join("\n"));
JavaMain.java · 53 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());         // 놀이 내내 살아 있는 값: 반복문 밖에 선언한다        int total = 0;        int bestRound = 0;        int bestScore = -1;        int streak = 0;            // 지금 이어지는 깨끗한 판 수        int longest = 0;        boolean prevClean = false; // 바로 앞 판이 깨끗했는가         StringBuilder out = new StringBuilder();        for (int r = 1; r <= n; r++) {            StringTokenizer st = new StringTokenizer(br.readLine());            int k = Integer.parseInt(st.nextToken());             // 이 판에서만 쓰는 값: 반복문 안에 선언하면 판마다 새로 생긴다            int miss = 0;            int score = 0;            for (int i = 1; i <= k; i++) {                int s = Integer.parseInt(st.nextToken());                score += s;                if (s == 0 && miss == 0) miss = i;   // i는 이 for 안에서만 보인다            }            if (prevClean) {                score *= 2;   // 여기에 int score를 또 쓰면 Java는 컴파일 오류다            }             total += score;            if (score > bestScore) {                bestScore = score;                bestRound = r;            }            boolean clean = miss == 0;            if (clean) {                streak++;                longest = Math.max(longest, streak);            } else {                streak = 0;            }            prevClean = clean;   // 이 판의 결과를 다 쓴 뒤에 넘긴다            out.append(score).append(' ').append(total).append(' ').append(miss).append('\n');        }        out.append(bestRound).append(' ').append(bestScore).append(' ').append(longest).append('\n');        System.out.print(out);    }}
C#Main.cs · 60 行
using System;using System.Text; class Program{    static void Main()    {        int n = int.Parse(Console.ReadLine().Trim());         // 놀이 내내 살아 있는 값: 반복문 밖에 선언한다        int total = 0;        int bestRound = 0;        int bestScore = -1;        int streak = 0;          // 지금 이어지는 깨끗한 판 수        int longest = 0;        bool prevClean = false;  // 바로 앞 판이 깨끗했는가         var sb = new StringBuilder();        for (int r = 1; r <= n; r++)        {            string[] parts = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            int k = int.Parse(parts[0]);             // 이 판에서만 쓰는 값: 반복문 안에 선언하면 판마다 새로 생긴다            int miss = 0;            int score = 0;            for (int i = 1; i <= k; i++)            {                int s = int.Parse(parts[i]);                score += s;                if (s == 0 && miss == 0) miss = i;   // i는 이 for 안에서만 보인다            }            if (prevClean)            {                score *= 2;   // 여기에 int score를 또 쓰면 C#은 컴파일 오류다 (CS0136)            }             total += score;            if (score > bestScore)            {                bestScore = score;                bestRound = r;            }            bool clean = miss == 0;            if (clean)            {                streak++;                longest = Math.Max(longest, streak);            }            else            {                streak = 0;            }            prevClean = clean;   // 이 판의 결과를 다 쓴 뒤에 넘긴다            sb.Append(score).Append(' ').Append(total).Append(' ').Append(miss).Append('\n');        }        sb.Append(bestRound).Append(' ').Append(bestScore).Append(' ').Append(longest).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 51 行
#include <algorithm>#include <iostream> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n;    std::cin >> n;     // 놀이 내내 살아 있는 값: 반복문 밖에 선언하고 처음 값을 꼭 넣는다    int total = 0;    int bestRound = 0;    int bestScore = -1;    int streak = 0;          // 지금 이어지는 깨끗한 판 수    int longest = 0;    bool prevClean = false;  // 바로 앞 판이 깨끗했는가     for (int r = 1; r <= n; r++) {        int k;        std::cin >> k;         // 이 판에서만 쓰는 값: 반복문 안에 선언하면 판마다 새로 시작한다        int miss = 0;        int score = 0;        for (int i = 1; i <= k; i++) {            int s;            std::cin >> s;            score += s;            if (s == 0 && miss == 0) miss = i;   // i는 이 for 안에서만 보인다        }        if (prevClean) {            score *= 2;   // 여기에 int score를 또 쓰면 바깥 score를 가리는 새 변수가 된다        }         total += score;        if (score > bestScore) {            bestScore = score;            bestRound = r;        }        const bool clean = miss == 0;        if (clean) {            streak++;            longest = std::max(longest, streak);        } else {            streak = 0;        }        prevClean = clean;   // 이 판의 결과를 다 쓴 뒤에 넘긴다        std::cout << score << ' ' << total << ' ' << miss << '\n';    }    std::cout << bestRound << ' ' << bestScore << ' ' << longest << '\n';}
Cmain.c · 49 行
#include <stdio.h> int main(void) {    int n;    scanf("%d", &n);     /* 놀이 내내 살아 있는 값: 반복문 밖에 선언하고 처음 값을 꼭 넣는다 */    int total = 0;    int best_round = 0;    int best_score = -1;    int streak = 0;       /* 지금 이어지는 깨끗한 판 수 */    int longest = 0;    int prev_clean = 0;   /* 바로 앞 판이 깨끗했는가 */     for (int r = 1; r <= n; r++) {        int k;        scanf("%d", &k);         /* 이 판에서만 쓰는 값: 반복문 안에 선언하면 판마다 새로 시작한다 */        int miss = 0;        int score = 0;        for (int i = 1; i <= k; i++) {            int s;            scanf("%d", &s);            score += s;            if (s == 0 && miss == 0) miss = i;   /* i는 이 for 안에서만 보인다 */        }        if (prev_clean) {            score *= 2;   /* 여기에 int score를 또 쓰면 바깥 score를 가리는 새 변수가 된다 */        }         total += score;        if (score > best_score) {            best_score = score;            best_round = r;        }        int clean = miss == 0;        if (clean) {            streak++;            if (streak > longest) longest = streak;        } else {            streak = 0;        }        prev_clean = clean;   /* 이 판의 결과를 다 쓴 뒤에 넘긴다 */        printf("%d %d %d\n", score, total, miss);    }    printf("%d %d %d\n", best_round, best_score, longest);    return 0;}
Craykonmain.cray · 50 行
n = 控制台.读行().转数字(); // 놀이 내내 살아 있는 값: 반복문 앞에서 먼저 만든다total = 0;bestRound = 0;bestScore = -1;streak = 0; // 지금 이어지는 깨끗한 판 수longest = 0;prevClean = 假; // 바로 앞 판이 깨끗했는가 循环 (r = 1; r <= n; r++){    줄 = 控制台.读数字();     // 이 판에서만 쓰는 값: 반복문 안에서 처음 만들면 판마다 새로 시작한다    miss = 0;    score = 0;    循环 (i = 1; i <= 줄[0]; i++)    {        score = score + 줄[i];        如果 (줄[i] == 0 && miss == 0)        {            miss = i; // i는 이 반복 안에서만 보인다        }    }    如果 (prevClean)    {        score = score * 2; // 이미 있는 이름에 넣으면 바깥 score가 바뀐다    }     total = total + score;    如果 (score > bestScore)    {        bestScore = score;        bestRound = r;    }    clean = miss == 0;    如果 (clean)    {        streak = streak + 1;        longest = 数学.较大值(longest, streak);    }    否则    {        streak = 0;    }    prevClean = clean; // 이 판의 결과를 다 쓴 뒤에 넘긴다    控制台.输出(score + " " + total + " " + miss);}控制台.输出(bestRound + " " + bestScore + " " + longest);