{ }codelper
メソッドP0346

自分の状態を文字列で伝えるゲームキャラクター

ゲームのキャラクターをクラスで作れ。
キャラクターのオブジェクトは、名前、最大体力 m、今の体力をフィールドに持つ。
今の体力は m から始まる。
キャラクターにはメソッドを三つ用意する。
hit(d) は体力を d だけ減らす。
ただし体力は 0 より小さくならず、0 で止まる。
heal(h) は体力を h だけ増やす。
ただし体力は m より大きくならず、m で止まる。
体力が 0 のキャラクターは倒れているので、回復を受けても何も変わらない。
状態文字列のメソッドは、キャラクターを "名前 体力/m" の形の文字列にして返す(例:ann 7/10)。
体力が 0 のときは、その後ろに空白一つと down を付ける(例:ann 0/10 down)。
状態文字列のメソッドは、その言語がオブジェクトを文字列に変えるときに呼ぶ名前で作ってみよう(Python __str__、Java・JS・TS toString、C# ToString、C++ operator<<)。
一行目にキャラクターの数 n と命令の数 q が与えられる。
続く n 行に、1 番から n 番までのキャラクターの名前と最大体力 m が一行に一つずつ与えられる。
続く q 行に、命令が一行に一つずつ与えられる。
hit i d は i 番のキャラクターの hit(d) を呼び、heal i h は i 番のキャラクターの heal(h) を呼ぶ。
命令を一つ処理するたびに、i 番のキャラクターの状態文字列を一行に出力せよ。
すべての命令を処理したあと、最後の行に倒れていない(体力が 1 以上の)キャラクターの数を出力せよ。
採点は出力だけを見るので、メソッドの分け方は検査しない。

入力
1 ≤ n ≤ 100、1 ≤ q ≤ 1,000、1 ≤ m ≤ 1,000、1 ≤ i ≤ n、1 ≤ d ≤ 1,000、1 ≤ h ≤ 1,000(すべて整数で、カンマなしの数字だけで与えられる)。
名前は英小文字・数字 1〜10 文字で、同じ名前のキャラクターが複数いることがある。
一行の中の値は空白一つで区切る
出力
命令ごとに一行ずつ q 行、そして最後の行に倒れていないキャラクターの数
例 1 · stdin
2 6
ann 10
bo 5
hit 1 3
heal 1 5
hit 2 9
heal 2 3
hit 1 4
hit 2 1
stdout
ann 7/10
ann 10/10
bo 0/5 down
bo 0/5 down
ann 6/10
bo 0/5 down
1
main.py
入力(stdin)

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

$ 準備完了

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

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

この問題は、クラスとメソッドがある Python・JavaScript・TypeScript・Java・C#・C++ だけを受け付けます(C にはメソッドがなく、Craykon はまだクラスを作れません)。
メソッドは、オブジェクトが持つ値を変えたり(hit、heal)、その値から答えを作って返したり(状態文字列)する関数です。
オブジェクトを文字列に変えるメソッドは、言語ごとに名前が決まっています。
Python は __str__ を作ると print(hero)・str(hero) がそれを呼びます。
Java は toString() を作ると System.out.println(hero) と "" + hero が、C# は override string ToString() を作ると Console.WriteLine(hero) と $"{hero}" が呼びます。
JS・TS は toString() を作ると ` ${hero} ・String(hero)・"" + hero` が呼びます。
C++ にはそういうメソッドがないので、operator<< を作って cout << hero と書けるようにします。
罠その一:JS の console.log(hero) は toString を呼ばずにオブジェクトの中身を表示するので、例の一行目が Hero { name: 'ann', maxHp: 10, hp: 7 } になります。
罠その二:C# で override を付けずに public string ToString() と書くと、警告が出るだけでコンパイルは通ります。
ところが StringBuilder.Append(hero) は元の object.ToString() を呼ぶので、どの行もクラス名の Hero だけになります。
罠その三:Java で名前を tostring() のように違えて書くと、ただの新しいメソッドになり、Main$Hero@55f96302 のような文字列が出ます。
メソッドの上に @Override を付けておけば、この書き間違いをコンパイルエラーで教えてくれます。
罠その四:体力を 0 と m の間に収める処理はメソッドの中で行います。
hp - d をそのまま入れると例の三行目が bo -4/5 になり、hp + h をそのまま入れると二行目が ann 12/10 になります。
Python は max(0, …)・min(m, …)、JS・TS・Java は Math.max・Math.min、C# は Math.Max・Math.Min、C++ は std::max・std::min を使います。
罠その五:倒れているかどうかは heal の中で先に確かめます。
確かめないと例の四行目が bo 3/5 になり、倒れたキャラクターがまた起き上がります。

Pythonmain.py · 47 行
import sys  class Hero:    def __init__(self, name, max_hp):        self.name = name        self.max_hp = max_hp        self.hp = max_hp  # 체력은 가득 찬 채로 시작한다     def hit(self, d):        self.hp = max(0, self.hp - d)  # 0보다 작아지지 않는다     def heal(self, h):        if self.hp == 0:  # 쓰러진 캐릭터는 회복하지 못한다            return        self.hp = min(self.max_hp, self.hp + h)     def is_down(self):        return self.hp == 0     # print(hero)·str(hero)·f"{hero}"가 부르는 메서드    def __str__(self):        text = f"{self.name} {self.hp}/{self.max_hp}"        return text + " down" if self.is_down() else text  def main():    lines = sys.stdin.read().split("\n")    n, q = map(int, lines[0].split())    heroes = []    for i in range(1, n + 1):        name, m = lines[i].split()        heroes.append(Hero(name, int(m)))    out = []    for i in range(n + 1, n + 1 + q):        cmd, idx, amount = lines[i].split()        hero = heroes[int(idx) - 1]        if cmd == "hit":            hero.hit(int(amount))        else:            hero.heal(int(amount))        out.append(str(hero))    out.append(str(sum(1 for h in heroes if not h.is_down())))    print("\n".join(out))  main()
JavaScriptmain.js · 44 行
class Hero {  constructor(name, maxHp) {    this.name = name;    this.maxHp = maxHp;    this.hp = maxHp; // 체력은 가득 찬 채로 시작한다  }   hit(d) {    this.hp = Math.max(0, this.hp - d); // 0보다 작아지지 않는다  }   heal(h) {    if (this.hp === 0) return; // 쓰러진 캐릭터는 회복하지 못한다    this.hp = Math.min(this.maxHp, this.hp + h);  }   isDown() {    return this.hp === 0;  }   // `${hero}`·String(hero)·"" + hero 가 부르는 메서드  toString() {    const text = `${this.name} ${this.hp}/${this.maxHp}`;    return this.isDown() ? text + " down" : text;  }} const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const heroes = [];for (let i = 1; i <= n; i++) {  const [name, m] = lines[i].trim().split(" ");  heroes.push(new Hero(name, Number(m)));}const out = [];for (let i = n + 1; i <= n + q; i++) {  const [cmd, idx, amount] = lines[i].trim().split(" ");  const hero = heroes[Number(idx) - 1];  if (cmd === "hit") hero.hit(Number(amount));  else hero.heal(Number(amount));  out.push(`${hero}`); // console.log(hero)는 toString을 부르지 않는다}out.push(String(heroes.filter((h) => !h.isDown()).length));console.log(out.join("\n"));
TypeScriptmain.ts · 46 行
import { readFileSync } from "node:fs"; class Hero {  private hp: number;   constructor(private readonly name: string, private readonly maxHp: number) {    this.hp = maxHp; // 체력은 가득 찬 채로 시작한다  }   hit(d: number): void {    this.hp = Math.max(0, this.hp - d); // 0보다 작아지지 않는다  }   heal(h: number): void {    if (this.hp === 0) return; // 쓰러진 캐릭터는 회복하지 못한다    this.hp = Math.min(this.maxHp, this.hp + h);  }   isDown(): boolean {    return this.hp === 0;  }   // `${hero}`·String(hero)가 부르는 메서드  toString(): string {    const text = `${this.name} ${this.hp}/${this.maxHp}`;    return this.isDown() ? text + " down" : text;  }} const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, q] = lines[0].trim().split(" ").map(Number);const heroes: Hero[] = [];for (let i = 1; i <= n; i++) {  const [name, m] = lines[i].trim().split(" ");  heroes.push(new Hero(name, Number(m)));}const out: string[] = [];for (let i = n + 1; i <= n + q; i++) {  const [cmd, idx, amount] = lines[i].trim().split(" ");  const hero = heroes[Number(idx) - 1];  if (cmd === "hit") hero.hit(Number(amount));  else hero.heal(Number(amount));  out.push(`${hero}`); // console.log(hero)는 toString을 부르지 않는다}out.push(String(heroes.filter((h) => !h.isDown()).length));console.log(out.join("\n"));
JavaMain.java · 64 行
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.StringTokenizer; public class Main {    static class Hero {        private final String name;        private final int maxHp;        private int hp;         Hero(String name, int maxHp) {            this.name = name;            this.maxHp = maxHp;            this.hp = maxHp; // 체력은 가득 찬 채로 시작한다        }         void hit(int d) {            hp = Math.max(0, hp - d); // 0보다 작아지지 않는다        }         void heal(int h) {            if (hp == 0) return; // 쓰러진 캐릭터는 회복하지 못한다            hp = Math.min(maxHp, hp + h);        }         boolean isDown() {            return hp == 0;        }         // println(hero)와 "" + hero 가 부르는 메서드. @Override가 이름 오타를 잡아 준다        @Override        public String toString() {            String text = name + " " + hp + "/" + maxHp;            return isDown() ? text + " down" : text;        }    }     public static void main(String[] args) throws Exception {        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(br.readLine());        int n = Integer.parseInt(st.nextToken());        int q = Integer.parseInt(st.nextToken());        Hero[] heroes = new Hero[n];        for (int i = 0; i < n; i++) {            st = new StringTokenizer(br.readLine());            String name = st.nextToken();            heroes[i] = new Hero(name, Integer.parseInt(st.nextToken()));        }        StringBuilder sb = new StringBuilder();        for (int i = 0; i < q; i++) {            st = new StringTokenizer(br.readLine());            String cmd = st.nextToken();            Hero hero = heroes[Integer.parseInt(st.nextToken()) - 1];            int amount = Integer.parseInt(st.nextToken());            if (cmd.equals("hit")) hero.hit(amount);            else hero.heal(amount);            sb.append(hero).append('\n'); // append(객체)가 toString을 부른다        }        int up = 0;        for (Hero h : heroes) if (!h.isDown()) up++;        sb.append(up).append('\n');        System.out.print(sb);    }}
C#Main.cs · 65 行
using System;using System.Text; class Hero{    private readonly string name;    private readonly int maxHp;    private int hp;     public Hero(string name, int maxHp)    {        this.name = name;        this.maxHp = maxHp;        hp = maxHp; // 체력은 가득 찬 채로 시작한다    }     public void Hit(int d)    {        hp = Math.Max(0, hp - d); // 0보다 작아지지 않는다    }     public void Heal(int h)    {        if (hp == 0) return; // 쓰러진 캐릭터는 회복하지 못한다        hp = Math.Min(maxHp, hp + h);    }     public bool IsDown => hp == 0;     // Console.WriteLine(hero)와 $"{hero}"가 부르는 메서드. override를 빼면 불리지 않는다    public override string ToString()    {        string text = $"{name} {hp}/{maxHp}";        return IsDown ? text + " down" : text;    }} class Program{    static void Main()    {        string[] first = Console.ReadLine()!.Trim().Split(' ');        int n = int.Parse(first[0]), q = int.Parse(first[1]);        var heroes = new Hero[n];        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            heroes[i] = new Hero(p[0], int.Parse(p[1]));        }        var sb = new StringBuilder();        for (int i = 0; i < q; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            Hero hero = heroes[int.Parse(p[1]) - 1];            int amount = int.Parse(p[2]);            if (p[0] == "hit") hero.Hit(amount);            else hero.Heal(amount);            sb.Append(hero).Append('\n'); // Append(객체)가 ToString을 부른다        }        int up = 0;        foreach (Hero h in heroes) if (!h.IsDown) up++;        sb.Append(up).Append('\n');        Console.Write(sb);    }}
C++main.cpp · 58 行
#include <algorithm>#include <iostream>#include <string>#include <vector>using namespace std; class Hero {    string name_;    int max_hp_;    int hp_; public:    Hero(const string& name, int max_hp) : name_(name), max_hp_(max_hp), hp_(max_hp) {}     void hit(int d) { hp_ = max(0, hp_ - d); }  // 0보다 작아지지 않는다     void heal(int h) {        if (hp_ == 0) return;  // 쓰러진 캐릭터는 회복하지 못한다        hp_ = min(max_hp_, hp_ + h);    }     bool is_down() const { return hp_ == 0; }     // C++에는 toString이 없다. cout << hero 가 되도록 << 연산자를 만들어 준다    friend ostream& operator<<(ostream& os, const Hero& h) {        os << h.name_ << ' ' << h.hp_ << '/' << h.max_hp_;        if (h.is_down()) os << " down";        return os;    }}; int main() {    ios::sync_with_stdio(false);    cin.tie(nullptr);    int n, q;    cin >> n >> q;    vector<Hero> heroes;    for (int i = 0; i < n; i++) {        string name;        int m;        cin >> name >> m;        heroes.emplace_back(name, m);    }    for (int i = 0; i < q; i++) {        string cmd;        int idx, amount;        cin >> cmd >> idx >> amount;        Hero& hero = heroes[idx - 1];  // &가 없으면 복사본을 고치게 된다        if (cmd == "hit") hero.hit(amount);        else hero.heal(amount);        cout << hero << '\n';    }    int up = 0;    for (const Hero& h : heroes)        if (!h.is_down()) up++;    cout << up << '\n';    return 0;}