C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++で敵を倒すための武器の最小使用回数を求める方法

問題の概要

n個の要素を持つ配列Aと、整数Hが与えられます。ここで、Hは敵の体力(HP)を表します。こちらはn個の武器を持っており、i番目の武器のダメージ力はA[i]です。これらの武器を組み合わせて敵を倒しますが、同じ武器を連続して2回使用することはできません。この制約のもとで、敵を倒すために必要な武器の使用回数の最小値を求めます。

例えば、入力がA = [2, 1, 7]、H = 11の場合を考えてみましょう。このときの出力は3となります。ダメージ7の武器を使用し、続いてダメージ2の武器、そして再びダメージ7の武器を使うことで敵を倒せるからです。

解法のアプローチ

この問題を効率的に解くには、以下の手順に従います。

sort the array A
n := size of A
x := (A[n - 1] + A[n - 2])
return H / x * 2 + (H mod x + A[n - 1] - 1) / A[n-1]

まず配列Aを昇順にソートします。すると、最も強い武器はA[n-1]、2番目に強い武器はA[n-2]になります。同じ武器を連続で使えないため、最適な戦略は「最強の武器」と「2番目に強い武器」を交互に使い続けることです。このとき、2回の攻撃で与えられる最大ダメージは x = A[n-1] + A[n-2] となります。

H / x * 2 の部分は、完全なペア攻撃(最強+2番目)を何セット行えるかを表しています。残りの (H mod x + A[n - 1] - 1) / A[n - 1] は、余ったダメージを埋めるために追加で必要な攻撃回数を切り上げ除算で計算する部分です。

C++実装例

理解を深めるために、以下の実装例を見てみましょう。

#include <bits/stdc++.h>
using namespace std;
int solve(vector<int> A, int H){
    sort(A.begin(), A.end());
    int n = A.size();
    int x = (A[n - 1] + A[n - 2]);
    return H / x * 2 + (H % x + A[n - 1] - 1) / A[n - 1];
}
int main(){
    vector<int> A = { 2, 1, 7 };
    int H = 11;
    cout << solve(A, H) << endl;
}

この実装では、ソートに O(n log n)、その後の計算は定数時間で完了するため、全体として非常に効率的です。

入力例

{ 2, 1, 7 }, 11

出力例

3
  1. C++で解くナイトの最短移動回数問題:メモ化再帰による効率的な解法

    問題概要無限に広がるチェス盤を考えます。座標は -∞ ~ +∞ の範囲に及び、ナイトは初期状態でマス [0, 0] に配置されています。ナイトの移動は下図のように8通りあり、それぞれ「縦または横の方向に2マス、その後それと直交する方向に1マス」という動きになります。この問題では、ナイトを目標のマス [x, y] まで移動させるのに必要な最小手数を求めます。なお、必ず目的地に到達できる(解が存在する)ことが保証されています。具体例たとえば入力が x = 5、y = 5 の場合、出力は 4 になります。これは次のような経路で到達できるためです。[0,0] → [2,1] → [4,2] → [3,

  2. C++の二分探索木(BST)で最小値のノードを見つける方法

    二分探索木(Binary Search Tree、BST)が与えられたとき、その木の中から最小の要素を見つけることを考えます。例えば、以下のようなBSTがあるとします。この場合、最小要素は 1 になります。考え方二分探索木の重要な性質として、左部分木には必ず親ノードより小さい値が格納されるというものがあります。この性質を利用すると、次の手順で最小要素を見つけることができます。ルートノードから探索を開始します。現在のノードの左の子が NULL でない間、左の子へ移動を繰り返します。左の子が NULL になったノードの値が、木全体の中で最小の要素です。この操作の計算量は木の高さに依存し、平衡な二分