{ }codelper
定数オーバーフロー整数の割り算P0387

データプランの超過料金

ある通信会社の料金プランは、1 か月にデータを 3 GB 500 MB まで基本で使える。
基本の量を超えて使ったデータは 100 MB のまとまりで数える。
超えた量がまとまり一つに満たなくても、まとまり一つと数える。
まとまり一つごとに 550 ウォンを追加で払う。
使った量が基本の量と同じかそれより少なければ、超えたまとまりは 0 個で、追加料金も 0 ウォンである。
単位は 1 KB = 1,024 B、1 MB = 1,024 KB、1 GB = 1,024 MB である。
1 か月の使用記録が n 個与えられるので、使ったデータの合計、超えたまとまりの数、追加料金を求める。

入力
1 行目に使用記録の数 n が与えられる(1 ≤ n ≤ 300)。
続く n 行に、使った量を表す整数 a と単位 u が空白一つで区切って与えられる(1 ≤ a ≤ 1,023)。
u は B、KB、MB、GB のどれかである。
出力
1 行目に使ったデータの合計を B 単位の整数で出力する。
2 行目に超えたまとまりの数を出力する。
3 行目に追加料金をウォン単位の整数で出力する。
三つの数はどれもコンマなしで数字だけを出力する。
例 1 · stdin
4
3 GB
900 MB
512 KB
100 B
stdout
4165468260
5
2750
例 2 · stdin
2
600 MB
3 GB
stdout
3850371072
1
550
main.py
入力(stdin)

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

$ 準備完了

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

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

変わらない値に名前を付けておく定数の作り方が言語ごとに違います。
Python には定数の文法がないので、KB = 1024 のように大文字の名前で書いておき、変えないと約束します。
もう一度値を入れてもエラーになりません。
JavaScript の const はもう一度値を入れると実行中に TypeError になり、TypeScript はコンパイルエラーで前もって止めます。
Java は static final、C# は const、C++ は constexpr を使い、三つとももう一度値を入れるとコンパイルエラーです。
C は #define KB 1024LL のようにマクロで置きますが、マクロは値ではなく文字をそのまま置き換えます。
Craykon には定数がないので、変えないと約束した変数で置きます。
MB = 1024 * KB のように定数を別の定数から作っておくと、1,024 という数を一か所にだけ書けば済みます。
罠その一: 定数式のあふれです。
3 GB は 3,221,225,472 B なので 32 ビット整数(最大 2,147,483,647)に入りません。
Java で static final long ALLOWANCE = 3 * 1024 * 1024 * 1024 + 500 * 1024 * 1024; と書くと、long に入れる前に int どうしで掛けるので、エラーなしで -549,453,824 になります。
例 1 でまとまりの数が 5 ではなく 45 になります。
3 * 1024 * 1024 * 1024 を C# の const と C++ の constexpr はコンパイルエラーで止め、C は警告だけでコンパイルされます(結果は決まっていません)。
1024L のように最初の値を 64 ビット整数で書けば直ります。
Python、JavaScript、Craykon はこの大きさの整数ではあふれません。
罠その二: かっこのないマクロです。
C で #define ALLOWANCE 3 * GB + 500 * MB と書くと、total - ALLOWANCE が total - 3 * GB + 500 * MB に展開されて、500 MB を引かずに足してしまいます。
例 1 でまとまりの数が 15 になります。
罠その三: 単位です。
この問題の 1 KB は 1,000 B ではなく 1,024 B です。
定数の一行を 1000 と書くと、例 1 の 1 行目が 4165468260 ではなく 3900512100 になります。
罠その四: 切り上げです。
例 1 は超えた量が 419,954,788 B で、まとまり 4 個を少し超えるので 5 個です。
切り捨てると 4 になります。
罠その五: ちょうど割り切れるときです。
例 2 は超えた量がちょうど 100 MB なので 1 個です。
切り上げを「商 + 1」で計算すると 2 になります。

Pythonmain.py · 24 行
import sys # 파이썬에는 상수 문법이 없다: 대문자 이름은 "바꾸지 말자"는 약속일 뿐이다KB = 1024MB = 1024 * KBGB = 1024 * MBALLOWANCE = 3 * GB + 500 * MB  # 기본으로 주는 양BLOCK = 100 * MB               # 넘은 양을 세는 묶음PRICE = 550                    # 묶음 하나의 요금(원)UNIT = {"B": 1, "KB": KB, "MB": MB, "GB": GB} lines = sys.stdin.read().split("\n")n = int(lines[0])total = 0for i in range(1, n + 1):    a, u = lines[i].split()    total += int(a) * UNIT[u] blocks = 0if total > ALLOWANCE:    blocks = (total - ALLOWANCE + BLOCK - 1) // BLOCK  # 올림print(total)print(blocks)print(blocks * PRICE)
JavaScriptmain.js · 24 行
// const는 다시 넣으면 실행 중에 TypeError가 난다const KB = 1024;const MB = 1024 * KB;const GB = 1024 * MB;const ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양const BLOCK = 100 * MB; // 넘은 양을 세는 묶음const PRICE = 550; // 묶음 하나의 요금(원)const UNIT = { B: 1, KB: KB, MB: MB, GB: GB }; const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);let total = 0;for (let i = 1; i <= n; i++) {  const [a, u] = lines[i].trim().split(" ");  total += Number(a) * UNIT[u];} let blocks = 0;if (total > ALLOWANCE) {  blocks = Math.floor((total - ALLOWANCE + BLOCK - 1) / BLOCK); // 올림}console.log(String(total));console.log(String(blocks));console.log(String(blocks * PRICE));
TypeScriptmain.ts · 26 行
import { readFileSync } from "node:fs"; // const는 다시 넣으면 컴파일 오류다const KB: number = 1024;const MB: number = 1024 * KB;const GB: number = 1024 * MB;const ALLOWANCE: number = 3 * GB + 500 * MB; // 기본으로 주는 양const BLOCK: number = 100 * MB; // 넘은 양을 세는 묶음const PRICE: number = 550; // 묶음 하나의 요금(원)const UNIT: Record<string, number> = { B: 1, KB: KB, MB: MB, GB: GB }; const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);let total: number = 0;for (let i = 1; i <= n; i++) {  const [a, u] = lines[i].trim().split(" ");  total += Number(a) * UNIT[u];} let blocks: number = 0;if (total > ALLOWANCE) {  blocks = Math.floor((total - ALLOWANCE + BLOCK - 1) / BLOCK); // 올림}console.log(String(total));console.log(String(blocks));console.log(String(blocks * PRICE));
JavaMain.java · 40 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public class Main {    // static final 상수. 1024L처럼 L을 붙여야 곱셈이 long으로 계산된다    static final long KB = 1024L;    static final long MB = 1024 * KB;    static final long GB = 1024 * MB;    static final long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양    static final long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음    static final long PRICE = 550;                   // 묶음 하나의 요금(원)     static long unit(String u) {        switch (u) {            case "KB": return KB;            case "MB": return MB;            case "GB": return GB;            default: return 1;        }    }     public static void main(String[] args) throws IOException {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        int n = Integer.parseInt(br.readLine().trim());        long total = 0;        for (int i = 0; i < n; i++) {            String[] parts = br.readLine().trim().split(" ");            total += Long.parseLong(parts[0]) * unit(parts[1]);        }         long blocks = 0;        if (total > ALLOWANCE) {            blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림        }        System.out.println(total);        System.out.println(blocks);        System.out.println(blocks * PRICE);    }}
C#Main.cs · 43 行
using System; class Program{    // const 상수: 컴파일할 때 값이 정해진다    const long KB = 1024L;    const long MB = 1024 * KB;    const long GB = 1024 * MB;    const long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양    const long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음    const long PRICE = 550;                   // 묶음 하나의 요금(원)     static long Unit(string u)    {        switch (u)        {            case "KB": return KB;            case "MB": return MB;            case "GB": return GB;            default: return 1;        }    }     static void Main()    {        int n = int.Parse(Console.ReadLine().Trim());        long total = 0;        for (int i = 0; i < n; i++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            total += long.Parse(parts[0]) * Unit(parts[1]);        }         long blocks = 0;        if (total > ALLOWANCE)        {            blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림        }        Console.WriteLine(total);        Console.WriteLine(blocks);        Console.WriteLine(blocks * PRICE);    }}
C++main.cpp · 34 行
#include <iostream>#include <string>using namespace std; // constexpr 상수: 컴파일할 때 계산되고, 넘치면 컴파일 오류다constexpr long long KB = 1024;constexpr long long MB = 1024 * KB;constexpr long long GB = 1024 * MB;constexpr long long ALLOWANCE = 3 * GB + 500 * MB; // 기본으로 주는 양constexpr long long BLOCK = 100 * MB;              // 넘은 양을 세는 묶음constexpr long long PRICE = 550;                   // 묶음 하나의 요금(원) int main() {    int n;    cin >> n;    long long total = 0;    for (int i = 0; i < n; i++) {        long long a;        string u;        cin >> a >> u;        long long unit = 1;        if (u == "KB") unit = KB;        else if (u == "MB") unit = MB;        else if (u == "GB") unit = GB;        total += a * unit;    }     long long blocks = 0;    if (total > ALLOWANCE) {        blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; // 올림    }    cout << total << '\n' << blocks << '\n' << blocks * PRICE << '\n';    return 0;}
Cmain.c · 33 行
#include <stdio.h>#include <string.h> /* #define은 글자를 그대로 바꿔 끼운다: 식은 꼭 괄호로 싼다 */#define KB 1024LL#define MB (1024 * KB)#define GB (1024 * MB)#define ALLOWANCE (3 * GB + 500 * MB) /* 기본으로 주는 양 */#define BLOCK (100 * MB)              /* 넘은 양을 세는 묶음 */#define PRICE 550                     /* 묶음 하나의 요금(원) */ int main(void) {    int n;    if (scanf("%d", &n) != 1) return 1;    long long total = 0;    for (int i = 0; i < n; i++) {        long long a;        char u[4];        if (scanf("%lld %3s", &a, u) != 2) return 1;        long long unit = 1;        if (strcmp(u, "KB") == 0) unit = KB;        else if (strcmp(u, "MB") == 0) unit = MB;        else if (strcmp(u, "GB") == 0) unit = GB;        total += a * unit;    }     long long blocks = 0;    if (total > ALLOWANCE) {        blocks = (total - ALLOWANCE + BLOCK - 1) / BLOCK; /* 올림 */    }    printf("%lld\n%lld\n%lld\n", total, blocks, blocks * PRICE);    return 0;}
Craykonmain.cray · 38 行
// 크레이콘에는 상수가 없다: 바꾸지 않기로 약속한 변수로 둔다킬로 = 1024;메가 = 1024 * 킬로;기가 = 1024 * 메가;기본량 = 3 * 기가 + 500 * 메가; // 기본으로 주는 양묶음 = 100 * 메가; // 넘은 양을 세는 묶음묶음요금 = 550; // 묶음 하나의 요금(원) 기록수 = コンソール.行読み().数に();합 = 0;繰り返す (i = 0; i < 기록수; i++){    칸들 = コンソール.行読み().空白除去().分ける();    양 = 칸들[0].数に();    단위 = 1;    もし (칸들[1] == "KB")    {        단위 = 킬로;    }    でなければ もし (칸들[1] == "MB")    {        단위 = 메가;    }    でなければ もし (칸들[1] == "GB")    {        단위 = 기가;    }    합 += 양 * 단위;} 묶음수 = 0;もし (합 > 기본량){    묶음수 = 数学.商(합 - 기본량 + 묶음 - 1, 묶음); // 올림}コンソール.書く(합);コンソール.書く(묶음수);コンソール.書く(묶음수 * 묶음요금);