{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

'일단 한 번 보내고, 끝에서 다시 보낼지 묻는' 흐름이라 do-while이 딱 맞아요: C·C++·Java·C#·JS·TS do { … } while (status == null);.
Python과 크레이콘에는 do-while이 없어서 while True: … break(크레이콘 동안 (참) … 그만;)로 '적어도 한 번'을 만들어요.
함정은 while (W + w <= T) { … }처럼 조건을 먼저 묻는 거예요.
그러면 예산이 첫 대기보다 작을 때(예시 넷째 줄 T = 0) 요청을 한 번도 보내지 않아요.
상한은 삼항 연산자 한 줄로: C 계열·JS·TS w = w * 2 > c ? c : w * 2;, 크레이콘도 같은 모양 대기 * 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·크레이콘은 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;        }        만약 (상태 != "")        {            그만;        }    }    콘솔.쓰기(상태 + " " + 시도 + " " + 기다린합);}