{ }codelper
do-while 循环模拟溢出P0264

掷出 6 就再掷一次的骰子游戏

进行若干局骰子游戏。
一局有 n 个人,从 1 号到 n 号轮流各进行一轮(n 号之后又是 1 号)。
所有人都从 0 分开始。
骰子是按固定规则生成数的装置,一局中所有人共用一个。
骰子里有当前值 x。
每掷一次,就把 x 换成 (a × x + c) 除以 m 的余数,新的 x 除以 6 的余数加 1 就是掷出的点数。
轮到一个人时,做以下事情。
1.
掷一次骰子,把点数加到本轮的和上。
2.
若点数是 6 且本轮掷的次数少于 3,回到 1。
3.
若本轮掷出了三次 6(三次都是 6),本轮的和变为 0。
4.
把本轮的和加到这个人的分数上。
分数达到 G 或以上时,这个人获胜,这一局结束。
只在一轮结束后判断是否获胜。
因此每轮至少掷一次。
第 T 轮结束后仍无人获胜,则这一局无人获胜而结束。
第一行给出局数 t,接下来 t 行每行给出一局的 n、G、T、初始 x、a、c、m,用一个空格隔开。
每局输出两行。
第一行写获胜者的编号(无人获胜则为 0)、进行的轮数、掷骰子的次数,用一个空格隔开。
第二行写 1 号到 n 号的分数,用一个空格隔开。

输入
1 ≤ t ≤ 50,1 ≤ n ≤ 8,1 ≤ G ≤ 10,000,1 ≤ T ≤ 10,000,所有局的 T 之和 ≤ 100,000。
1 ≤ m ≤ 1,000,000,0 ≤ 初始 x、a、c < m(整数)
输出
每局两行
样例 1 · stdin
2
2 15 20 1 9 10 19
1 5 3 5 1 0 7
stdout
1 11 15
15 10
0 3 9
0
main.py
输入(stdin)

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

$ 就绪

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

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

一轮的形状是“先掷,再问要不要再掷”,正好是 do-while。
Java、C#、C、C++、JavaScript、TypeScript 写成 do { 掷 } while (face == 6 && count < 3);,没有 do-while 的 Python 在 while True: 里掷完后在末尾 if not (...): break,Craykon 写成 当 (真) { … 如果 (!(…)) { 跳出; } }(韩文写作 동안 (참) { … 만약 (!(…)) { 그만; } })。
四个陷阱:① 把 do-while 改成普通的 while (face == 6 && count < 3) 会在进入前先判断,用还没掷过的 face(0)一次也不会掷。
② a × x 最大到 10^12。
在 C、C++、Java、C# 中用 int 相乘会溢出(C/C++ 是未定义行为,Java/C# 会悄悄回绕,甚至变成负数),点数就错了。
要用 long long/long 相乘。
Python 的 int 没有上限,JS/TS/Craykon 的数在 2^53(约 9×10^15)以内是精确的,所以 10^12 没问题。
③ 掷出三次 6 就在那里停下并得 0 分。
写成 count <= 3 会掷第四次,漏掉清零会加上 18 分。
循环结束后最后一个点数是 6,就说明三次都是 6,用一行 if (face == 6) 就能判断。
④ 只在一轮结束后判断是否获胜。
每掷一次就把分数加上本轮和去和 G 比较,会把即将因 6·6·6 变成 0 分的一轮也算作获胜。

Pythonmain.py · 34 行
import sys lines = sys.stdin.read().split("\n")t = int(lines[0])out = []for line in lines[1 : t + 1]:    n, goal, max_turns, x, a, c, m = map(int, line.split())    scores = [0] * n    winner = 0    turns = 0    rolls = 0    player = 0    while turns < max_turns and winner == 0:        turns += 1        turn_sum = 0        count = 0        # Python에는 do-while이 없다: while True로 들어가 끝에서 멈출지 묻는다(첫 굴림은 언제나 한다)        while True:            x = (a * x + c) % m            face = x % 6 + 1            rolls += 1            count += 1            turn_sum += face            if not (face == 6 and count < 3):                break        if face == 6:  # 세 번 모두 6이면 이번 차례는 0점            turn_sum = 0        scores[player] += turn_sum        if scores[player] >= goal:            winner = player + 1        player = (player + 1) % n    out.append(f"{winner} {turns} {rolls}")    out.append(" ".join(map(str, scores)))print("\n".join(out))
JavaScriptmain.js · 27 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const t = Number(lines[0]);const out = [];for (let g = 1; g <= t; g++) {  // a * x는 10^12까지라 number(2^53까지 정확)로 충분하다  let [n, goal, maxTurns, x, a, c, m] = lines[g].trim().split(" ").map(Number);  const scores = new Array(n).fill(0);  let winner = 0, turns = 0, rolls = 0, player = 0;  while (turns < maxTurns && winner === 0) {    turns++;    let turnSum = 0, count = 0, face;    do {      // 첫 굴림은 언제나 한다      x = (a * x + c) % m;      face = (x % 6) + 1;      rolls++;      count++;      turnSum += face;    } while (face === 6 && count < 3);    if (face === 6) turnSum = 0; // 세 번 모두 6    scores[player] += turnSum;    if (scores[player] >= goal) winner = player + 1;    player = (player + 1) % n;  }  out.push(`${winner} ${turns} ${rolls}`, scores.join(" "));}console.log(out.join("\n"));
TypeScriptmain.ts · 30 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const t: number = Number(lines[0]);const out: string[] = [];for (let g = 1; g <= t; g++) {  // a * x는 10^12까지라 number(2^53까지 정확)로 충분하다  const [n, goal, maxTurns, x0, a, c, m] = lines[g].trim().split(" ").map(Number);  let x: number = x0;  const scores: number[] = new Array<number>(n).fill(0);  let winner = 0, turns = 0, rolls = 0, player = 0;  while (turns < maxTurns && winner === 0) {    turns++;    let turnSum = 0, count = 0, face: number;    do {      // 첫 굴림은 언제나 한다      x = (a * x + c) % m;      face = (x % 6) + 1;      rolls++;      count++;      turnSum += face;    } while (face === 6 && count < 3);    if (face === 6) turnSum = 0; // 세 번 모두 6    scores[player] += turnSum;    if (scores[player] >= goal) winner = player + 1;    player = (player + 1) % n;  }  out.push(`${winner} ${turns} ${rolls}`, scores.join(" "));}console.log(out.join("\n"));
JavaMain.java · 36 行
import java.io.BufferedReader;import java.io.InputStreamReader; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int t = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int g = 0; g < t; g++) {            String[] p = in.readLine().trim().split(" ");            int n = Integer.parseInt(p[0]), goal = Integer.parseInt(p[1]), maxTurns = Integer.parseInt(p[2]);            long x = Long.parseLong(p[3]), a = Long.parseLong(p[4]), c = Long.parseLong(p[5]), m = Long.parseLong(p[6]); // a * x는 int를 넘친다            int[] scores = new int[n];            int winner = 0, turns = 0, rolls = 0, player = 0;            while (turns < maxTurns && winner == 0) {                turns++;                int turnSum = 0, count = 0, face;                do { // 첫 굴림은 언제나 한다                    x = (a * x + c) % m;                    face = (int) (x % 6) + 1;                    rolls++;                    count++;                    turnSum += face;                } while (face == 6 && count < 3);                if (face == 6) turnSum = 0; // 세 번 모두 6                scores[player] += turnSum;                if (scores[player] >= goal) winner = player + 1;                player = (player + 1) % n;            }            out.append(winner).append(' ').append(turns).append(' ').append(rolls).append('\n');            for (int i = 0; i < n; i++) out.append(i > 0 ? " " : "").append(scores[i]);            out.append('\n');        }        System.out.print(out);    }}
C#Main.cs · 39 行
using System;using System.Text; class Program{    static void Main()    {        int t = int.Parse(Console.ReadLine()!.Trim());        var sb = new StringBuilder();        for (int g = 0; g < t; g++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            int n = int.Parse(p[0]), goal = int.Parse(p[1]), maxTurns = int.Parse(p[2]);            long x = long.Parse(p[3]), a = long.Parse(p[4]), c = long.Parse(p[5]), m = long.Parse(p[6]); // a * x는 int를 넘친다            int[] scores = new int[n];            int winner = 0, turns = 0, rolls = 0, player = 0;            while (turns < maxTurns && winner == 0)            {                turns++;                int turnSum = 0, count = 0, face;                do // 첫 굴림은 언제나 한다                {                    x = (a * x + c) % m;                    face = (int)(x % 6) + 1;                    rolls++;                    count++;                    turnSum += face;                } while (face == 6 && count < 3);                if (face == 6) turnSum = 0; // 세 번 모두 6                scores[player] += turnSum;                if (scores[player] >= goal) winner = player + 1;                player = (player + 1) % n;            }            sb.Append($"{winner} {turns} {rolls}\n");            sb.Append(string.Join(" ", scores)).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 33 行
#include <iostream>#include <vector> int main() {    std::ios::sync_with_stdio(false);    int t;    std::cin >> t;    while (t--) {        int n, goal, maxTurns;        long long x, a, c, m; // a * x는 10^12까지 커져서 int로는 넘친다        std::cin >> n >> goal >> maxTurns >> x >> a >> c >> m;        std::vector<int> scores(n, 0);        int winner = 0, turns = 0, rolls = 0, player = 0;        while (turns < maxTurns && winner == 0) {            turns++;            int turnSum = 0, count = 0, face;            do { // 첫 굴림은 언제나 한다                x = (a * x + c) % m;                face = static_cast<int>(x % 6) + 1;                rolls++;                count++;                turnSum += face;            } while (face == 6 && count < 3);            if (face == 6) turnSum = 0; // 세 번 모두 6            scores[player] += turnSum;            if (scores[player] >= goal) winner = player + 1;            player = (player + 1) % n;        }        std::cout << winner << ' ' << turns << ' ' << rolls << '\n';        for (int i = 0; i < n; i++) std::cout << (i ? " " : "") << scores[i];        std::cout << '\n';    }}
Cmain.c · 32 行
#include <stdio.h> int main(void) {    int t;    scanf("%d", &t);    while (t--) {        int n, goal, maxTurns;        long long x, a, c, m; /* a * x는 10^12까지 커져서 int(약 21억)로는 넘친다 */        scanf("%d %d %d %lld %lld %lld %lld", &n, &goal, &maxTurns, &x, &a, &c, &m);        int scores[8] = {0};        int winner = 0, turns = 0, rolls = 0, player = 0;        while (turns < maxTurns && winner == 0) {            turns++;            int turnSum = 0, count = 0, face;            do { /* 첫 굴림은 언제나 한다 */                x = (a * x + c) % m;                face = (int)(x % 6) + 1;                rolls++;                count++;                turnSum += face;            } while (face == 6 && count < 3);            if (face == 6) turnSum = 0; /* 세 번 모두 6 */            scores[player] += turnSum;            if (scores[player] >= goal) winner = player + 1;            player = (player + 1) % n;        }        printf("%d %d %d\n", winner, turns, rolls);        for (int i = 0; i < n; i++) printf(i ? " %d" : "%d", scores[i]);        putchar('\n');    }    return 0;}
Craykonmain.cray · 49 行
t = 控制台.读行().转数字();循环 (g = 0; g < t; g++){    p = 控制台.读行().去空白().分割();    n = p[0].转数字();    목표 = p[1].转数字();    최대차례 = p[2].转数字();    x = p[3].转数字();    a = p[4].转数字();    c = p[5].转数字();    m = p[6].转数字();    점수 = 新列表(n, 0);    이긴사람 = 0;    차례수 = 0;    굴린수 = 0;    사람 = 0;    当 (차례수 < 최대차례 && 이긴사람 == 0)    {        차례수++;        합 = 0;        번 = 0;        눈 = 0;        // 크레이콘에는 do-while이 없다: 동안 (참)으로 들어가 끝에서 멈출지 묻는다(첫 굴림은 언제나 한다)        当 (真)        {            x = (a * x + c) % m; // 10^12까지라 정확하다            눈 = x % 6 + 1;            굴린수++;            번++;            합 += 눈;            如果 (!(눈 == 6 && 번 < 3))            {                跳出;            }        }        如果 (눈 == 6)        {            합 = 0;        }        점수[사람] += 합;        如果 (점수[사람] >= 목표)        {            이긴사람 = 사람 + 1;        }        사람 = (사람 + 1) % n;    }    控制台.输出(이긴사람 + " " + 차례수 + " " + 굴린수);    控制台.输出(점수.连接(" "));}