1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

じゃんけんの手の出し方

Posted at

今回は 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 の範囲
  • 残りの指の本数から
    • チョキの回数 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 を決めれば他が一意に決まる

手順

  • p0 〜 5p ≤ m まで全探索
  • 残りの指本数は M - 5p
  • チョキは 2 本なので:
    • (M - 5p) が 偶数でないと不可能
  • よって:
c = (M - 5p) / 2
g = N - p - c
  • g < 0c < 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)だけで計算可能。
  • 以上に気づくと、
    自分がパー(またはチョキ)を出す回数を全探索するだけで、最大勝利数を求めることができる。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?