今回は paiza の「じゃんけんの手の出し方」の問題に挑戦!
問題概要
- あなたは
N回 じゃんけんをする - 相手が出す手(
G/C/P)は すべて事前に分かっている - ただし制約として:
- あなたが
N回で出した指の本数の合計がちょうどM本 - 指の本数の定義
- グー:0 本
- チョキ:2 本
- パー:5 本
- あなたが
- この条件を守りつつ、勝てる回数を最大化したい
👉 目的
「指の合計が M になるように手を選びながら、勝利数を最大にせよ」
入力例:
4 7
CGPC
出力例:
4
✅OK例:
const fs = require("fs");
// 標準入力を読み込む
const input = fs.readFileSync("/dev/stdin", "utf-8");
const lines = input.split("\n");
// n: じゃんけんの回数, m: 指の合計
const [n, m] = lines[0].split(" ").map(Number);
// 相手が出す手の文字列(G, C, P)
const s = lines[1];
// 文字 a が文字列 s に何回出てくるか数える関数
const count = (s, a) => {
let counter = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === a) counter += 1;
}
return counter;
};
// 相手が出す各手の回数
const gNum = count(s, "G"); // 相手のグーの回数
const cNum = count(s, "C"); // 相手のチョキの回数
const pNum = count(s, "P"); // 相手のパーの回数
let ans = 0; // 勝てる最大回数
// 自分が出すパーの回数 p を全探索
// パーは指を 5 本使うので、5p <= m の範囲で回す
for (let p = 0; p * 5 <= m; p++) {
// 残りの指の本数
// チョキは 2 本なので、残りが偶数でないとダメ
if ((m - 5 * p) % 2 !== 0) continue;
// チョキの回数
const c = (m - 5 * p) / 2;
// グーの回数(指 0 本)
const g = n - c - p;
// 回数が負になるのは不正
if (g < 0) continue;
// 勝てる回数を計算
// ・パーは相手のグーに勝つ
// ・チョキは相手のパーに勝つ
// ・グーは相手のチョキに勝つ
const win =
Math.min(p, gNum) + // パー vs グー
Math.min(c, pNum) + // チョキ vs パー
Math.min(g, cNum); // グー vs チョキ
// 最大値を更新
ans = Math.max(ans, win);
}
// 結果を出力
console.log(ans);
🔍 コードの流れ
- 入力を読み込み
-
n:じゃんけんの回数 -
m:使う指の合計 -
s:相手が出す手の列
-
- 相手が出す
- グー (
G) - チョキ (
C) - パー (
P)
それぞれの回数を数える
- グー (
- 答え(最大勝利数)を
ansに用意する - 自分が出す パーの回数
pを全て試す- パーは指を 5 本使うので
5p ≤ mの範囲
- パーは指を 5 本使うので
- 残りの指の本数から
- チョキの回数
c(2 本ずつ)を計算 - グーの回数
g = n - p - cを決める
- チョキの回数
-
g, c, pのどれかが不正(負)ならスキップ - 決めた (
g, c, p) で、実際に勝てる回数を-
min(自分の回数, 相手の回数)で計算
-
- 勝てる回数の最大値を更新
- 全探索が終わったら
ansを出力
💡考え方
● 重要な観察ポイント
- 勝敗は 回数の対応だけで決まる
- パーは相手のグーに勝つ
- チョキは相手のパーに勝つ
- グーは相手のチョキに勝つ
- じゃんけんの順番は関係ない
- 「何回グー・チョキ・パーを出すか」だけが重要
- 「何回グー・チョキ・パーを出すか」だけが重要
● 数式化するとどうなるか
- 自分の手の回数を
- グー:
g - チョキ:
c - パー:
p
- グー:
- 条件は次の 2 つ:
① 回数の合計
g + c + p = N
② 指の本数の合計
0*g + 2*c + 5*p = M
● 全探索の方針
- 3 変数すべてを探索する必要はない
- 出せる指の本数の合計が決まっているため、例えば、パーの回数
pを決めれば他が一意に決まる
手順
-
pを0 〜 5p ≤ mまで全探索 - 残りの指本数は
M - 5p - チョキは 2 本なので:
-
(M - 5p)が 偶数でないと不可能
-
- よって:
c = (M - 5p) / 2
g = N - p - c
-
g < 0やc < 0なら不正 → スキップ
● 勝てる回数の計算
- 相手の手の回数を数えておく
-
gNum:相手のグー -
cNum:相手のチョキ -
pNum:相手のパー
-
- 勝てる回数は:
min(p, gNum) // パー vs グー
+ min(c, pNum) // チョキ vs パー
+ min(g, cNum) // グー vs チョキ
- これを最大化する
例①:パー vs グー
状況
- 自分が パーを
p = 3回出す - 相手が グーを
gNum = 5回出す
考える
- パーはグーに勝つ
- でも:
- 自分のパーは
3回しかない - 相手のグーは
5回ある
- 自分のパーは
結果
- 勝てるのは 最大
3回
min(3, 5) = 3
👉 これが
min(p, gNum)
例②:チョキ vs パー
状況
- 自分の チョキ
c = 4回 - 相手の パー
pNum = 2回
考える
- チョキはパーに勝つ
- でも:
- 自分のチョキは
4回ある - 相手のパーは
2回しか出てこない
- 自分のチョキは
結果
- 勝てるのは
2回まで
min(4, 2) = 2
👉 これが
min(c, pNum)
📝まとめ
- じゃんけんは 順番を考える必要がなく、
勝敗は「どの手を何回出したか」だけで決まる。 - 出せる 指の本数の合計
Mが固定されているため、- パー(5 本)を出す回数を決めると
- 残りの指数から チョキ(2 本)の回数が一意に決まる
- その結果、グー(0 本)の回数も自動的に決まる。
- 勝てる回数は、自分の手の回数と、相手の負け手の回数の対応(
min)だけで計算可能。 - 以上に気づくと、
自分がパー(またはチョキ)を出す回数を全探索するだけで、最大勝利数を求めることができる。