{ }codelper
do-while ループ三項演算子オーバーフローP0138

待ってから送り直す

サーバーに要求を送るプログラムは、要求が失敗すると少し待ってから送り直す。
作業ごとに最初の待ち時間 b、最大待ち時間 c、最大試行回数 a、待ち時間の予算 T と結果の文字列 s が与えられる。
s の i 番目の文字は i 回目の試行の結果である。
S は成功、F は一時的な失敗(送り直せる)、X は永久的な失敗(送り直さない)である。
作業ごとに、最初は待ち時間 w = b、これまでに待った時間の合計 W = 0 である。
作業ごとに下の 1〜5 を繰り返す。
1.
要求を一回送る(最初の要求は T に関係なく必ず送る)。
2.
結果が S なら "ok" で、X なら "fatal" で終わる。
3.
結果が F で、これまでに a 回送っていれば "exhausted" で終わる。
4.
そうでなければ w だけ待つ。
ただし W + w が T より大きければ待たずに "timeout" で終わる(W + w が T と等しければ待つ)。
5.
待ったあと W に w を足し、w を二倍にする。
ただし二倍が c より大きければ w = c とする。
そして 1 に戻る。
一行目に作業の数 q が与えられ、続く q 行に作業ごとの b、c、a、T、s が空白一つずつを挟んで与えられる。
作業ごとに一行に、終わった状態(ok、fatal、exhausted、timeout のいずれかを引用符なしで)、要求を送った回数、W を空白一つずつ区切って、入力の順に出力せよ。

入力
1 ≤ q ≤ 1,000、1 ≤ b ≤ c ≤ 2,000,000,000、1 ≤ a ≤ s の長さ ≤ 100、0 ≤ T ≤ 10^12、s は S・F・X だけからなる
出力
q 行、各行に状態の語、送った回数、待った時間の合計
例 1 · stdin
5
100 1000 5 10000 FFSFF
100 250 4 10000 FFFFS
500 4000 4 1200 FFFS
1000 1000 3 0 SFF
10 100 3 1000 FXS
stdout
ok 3 300
exhausted 4 550
timeout 2 500
ok 1 0
fatal 2 10
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

「まず一回送って、最後にもう一度送るか聞く」流れなので do-while がぴったりです:C・C++・Java・C#・JS・TS do { … } while (status == null);。
Python と Craykon には do-while がないので、while True: … break(Craykon 間 (真) … 抜ける;)で「少なくとも一回」を作ります。
落とし穴は while (W + w <= T) { … } のように条件を先に聞くことです。
すると予算が最初の待ち時間より小さいとき(例の四行目 T = 0)、要求を一度も送りません。
上限は三項演算子一行で:C 系・JS・TS w = w * 2 > c ? c : w * 2;、Craykon も同じ形 待ち * 2 > 上限 ? 上限 : 待ち * 2、Python だけ順序が違って c if w * 2 > c else w * 2 です。
見かけは int で足りそうですが、c は 20 億までなので w * 2 は 40 億になり得て、待った合計は 99 × 20 億まで大きくなります。
C・C++ の int はあふれると未定義動作(たいてい負の数に回り込む)で、Java・C# の int は黙って負の数に回り込むので、w * 2 > c が偽になり待ち時間が負になります。
そこで C・C++ は long long、Java・C# は long で受け取ります。
Python の整数は大きさに制限がなく、JS・TS・Craykon は double なので 2^53 以内のこれらの値を正確に扱えます。
3(回数)と 4(予算)を聞く順を入れ替えると、最後の試行のあとで予算を調べてしまい "exhausted" が "timeout" に変わります。

Pythonmain.py · 31 行
import sys lines = sys.stdin.read().split("\n")q = int(lines[0])out = []for line in lines[1:q + 1]:    b, c, a, t, s = line.split()    c, a, t = int(c), int(a), int(t)    wait = int(b)  # 다음에 기다릴 시간    waited = 0     # 지금까지 기다린 시간의 합    tries = 0    # Python에는 do-while이 없다: while True로 들어가 끝에서 멈출지 정한다(첫 시도는 언제나 한다)    while True:        result = s[tries]        tries += 1        if result == "S":            status = "ok"            break        if result == "X":            status = "fatal"            break        if tries == a:            status = "exhausted"            break        if waited + wait > t:            status = "timeout"            break        waited += wait        wait = c if wait * 2 > c else wait * 2    out.append(f"{status} {tries} {waited}")print("\n".join(out))
JavaScriptmain.js · 28 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0]);const out = [];for (let j = 1; j <= q; j++) {  const [b, c, a, t, s] = lines[j].trim().split(/\s+/);  const cap = Number(c);  const maxTries = Number(a);  const budget = Number(t);  let wait = Number(b); // 다음에 기다릴 시간  let waited = 0; // 지금까지 기다린 시간의 합  let tries = 0;  let status = null;  // 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다  do {    const result = s[tries];    tries++;    if (result === "S") status = "ok";    else if (result === "X") status = "fatal";    else if (tries === maxTries) status = "exhausted";    else if (waited + wait > budget) status = "timeout";    else {      waited += wait;      wait = wait * 2 > cap ? cap : wait * 2;    }  } while (status === null);  out.push(`${status} ${tries} ${waited}`);}console.log(out.join("\n"));
TypeScriptmain.ts · 29 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q: number = Number(lines[0]);const out: string[] = [];for (let j = 1; j <= q; j++) {  const [b, c, a, t, s] = lines[j].trim().split(/\s+/);  const cap: number = Number(c);  const maxTries: number = Number(a);  const budget: number = Number(t);  let wait: number = Number(b); // 다음에 기다릴 시간  let waited = 0; // 지금까지 기다린 시간의 합  let tries = 0;  let status: string | null = null;  // 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다  do {    const result: string = s[tries];    tries++;    if (result === "S") status = "ok";    else if (result === "X") status = "fatal";    else if (tries === maxTries) status = "exhausted";    else if (waited + wait > budget) status = "timeout";    else {      waited += wait;      wait = wait * 2 > cap ? cap : wait * 2;    }  } while (status === null);  out.push(`${status} ${tries} ${waited}`);}console.log(out.join("\n"));
JavaMain.java · 37 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int q = Integer.parseInt(br.readLine().trim());        StringBuilder out = new StringBuilder();        for (int j = 0; j < q; j++) {            String[] p = br.readLine().trim().split("\\s+");            long wait = Long.parseLong(p[0]); // 다음에 기다릴 시간(2배가 int를 넘을 수 있다)            long cap = Long.parseLong(p[1]);            int maxTries = Integer.parseInt(p[2]);            long budget = Long.parseLong(p[3]);            String s = p[4];            long waited = 0; // 지금까지 기다린 시간의 합            int tries = 0;            String status = null;            // 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다            do {                char result = s.charAt(tries);                tries++;                if (result == 'S') status = "ok";                else if (result == 'X') status = "fatal";                else if (tries == maxTries) status = "exhausted";                else if (waited + wait > budget) status = "timeout";                else {                    waited += wait;                    wait = wait * 2 > cap ? cap : wait * 2;                }            } while (status == null);            out.append(status).append(' ').append(tries).append(' ').append(waited).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 40 行
using System;using System.Text; class Program{    static void Main()    {        int q = int.Parse(Console.ReadLine().Trim());        var sb = new StringBuilder();        for (int j = 0; j < q; j++)        {            string[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);            long wait = long.Parse(p[0]); // 다음에 기다릴 시간(2배가 int를 넘을 수 있다)            long cap = long.Parse(p[1]);            int maxTries = int.Parse(p[2]);            long budget = long.Parse(p[3]);            string s = p[4];            long waited = 0; // 지금까지 기다린 시간의 합            int tries = 0;            string? status = null;            // 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다            do            {                char result = s[tries];                tries++;                if (result == 'S') status = "ok";                else if (result == 'X') status = "fatal";                else if (tries == maxTries) status = "exhausted";                else if (waited + wait > budget) status = "timeout";                else                {                    waited += wait;                    wait = wait * 2 > cap ? cap : wait * 2;                }            } while (status == null);            sb.Append(status).Append(' ').Append(tries).Append(' ').Append(waited).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 30 行
#include <iostream>#include <string> int main() {    int q;    std::cin >> q;    for (int j = 0; j < q; j++) {        long long wait, cap, budget; // 2배가 int(약 21억)를 넘을 수 있다        int maxTries;        std::string s;        std::cin >> wait >> cap >> maxTries >> budget >> s;        long long waited = 0; // 지금까지 기다린 시간의 합        int tries = 0;        std::string status;        // 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다        do {            char result = s[tries];            tries++;            if (result == 'S') status = "ok";            else if (result == 'X') status = "fatal";            else if (tries == maxTries) status = "exhausted";            else if (waited + wait > budget) status = "timeout";            else {                waited += wait;                wait = wait * 2 > cap ? cap : wait * 2;            }        } while (status.empty());        std::cout << status << ' ' << tries << ' ' << waited << '\n';    }}
Cmain.c · 30 行
#include <stdio.h> int main(void) {    int q;    scanf("%d", &q);    for (int j = 0; j < q; j++) {        long long wait, cap, budget; /* 2배가 int(약 21억)를 넘을 수 있다 */        int max_tries;        char s[128];        scanf("%lld %lld %d %lld %127s", &wait, &cap, &max_tries, &budget, s);        long long waited = 0; /* 지금까지 기다린 시간의 합 */        int tries = 0;        const char *status = NULL;        /* 첫 시도는 언제나 한다: 몸통을 먼저 돌고 끝에서 계속할지 묻는다 */        do {            char result = s[tries];            tries++;            if (result == 'S') status = "ok";            else if (result == 'X') status = "fatal";            else if (tries == max_tries) status = "exhausted";            else if (waited + wait > budget) status = "timeout";            else {                waited += wait;                wait = wait * 2 > cap ? cap : wait * 2;            }        } while (status == NULL);        printf("%s %d %lld\n", status, tries, waited);    }    return 0;}
Craykonmain.cray · 45 行
q = コンソール.行読み().数に();繰り返す (j = 0; j < q; j++){    p = コンソール.行読み().空白除去().分ける();    대기 = p[0].数に(); // 다음에 기다릴 시간    상한 = p[1].数に();    최대시도 = p[2].数に();    예산 = p[3].数に();    결과들 = p[4];    기다린합 = 0;    시도 = 0;    상태 = "";    // 크레이콘에는 do-while이 없다: 동안 (참)으로 들어가서 끝에서 멈출지 묻는다(첫 시도는 언제나 한다)    間 (真)    {        결과 = 결과들[시도];        시도++;        もし (결과 == "S")        {            상태 = "ok";        }        でなければ もし (결과 == "X")        {            상태 = "fatal";        }        でなければ もし (시도 == 최대시도)        {            상태 = "exhausted";        }        でなければ もし (기다린합 + 대기 > 예산)        {            상태 = "timeout";        }        でなければ        {            기다린합 += 대기;            대기 = 대기 * 2 > 상한 ? 상한 : 대기 * 2;        }        もし (상태 != "")        {            抜ける;        }    }    コンソール.書く(상태 + " " + 시도 + " " + 기다린합);}