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

C++で解く「最も高いビルボード」問題 ― 動的計画法によるアプローチ


ビルボードを設置する際、その高さはできるだけ高くしたいものです。ビルボードは両側に2本の鋼製の支柱で支えられますが、それぞれの支柱は必ず同じ高さでなければなりません。また、溶接によって自由に接合できる棒(ロッド)のコレクションが与えられます。たとえば、長さ1、2、3のロッドがあれば、これらをつなぎ合わせて長さ6の支柱を作ることができます。ここでの課題は、ビルボードを支えられる最大の高さを求めることです。もしビルボードを支えることができない場合は0を返します。

たとえば、入力が [1,2,2,3,3,3,4] の場合、出力は9になります。これは、ロッドを [1,2,2,4](合計9)と [3,3,3](合計9)という2つのグループに分けられるためです。

解法の考え方

この問題は動的計画法(DP)を用いて解きます。ポイントとなるのは、左右の支柱の高さの差に着目することです。

dp[i][j] を「i本目までのロッドを使ったとき、左右の支柱の高さの差が j − 5000 である状態における、低い方の支柱の高さの最大値」と定義します。差は負の値にもなり得るため、オフセットとして5000を加えて配列のインデックスに対応させています。

各ロッドについては、「高い方の支柱に追加する」「低い方の支柱に追加する」「使わない」の3つの選択肢があり、これらをすべて試しながらDPテーブルを更新していきます。

アルゴリズムの手順

  • sum := 0、n := ロッドの数、N := 2 * 5000 とします

  • (n + 1) × (N + 1) のサイズを持つ2次元配列 dp を -1 で初期化します

  • dp[0, 5000] := 0 と設定します(差0・高さ0の初期状態)

  • i := 0 から i < n の間、以下を繰り返します

    • j := 0 から j <= N の間、以下を繰り返します

      • x := rods[i] とします

      • j − x >= 0 かつ dp[i, j − x] ≠ -1 の場合:dp[i + 1, j] = max(dp[i + 1, j], dp[i, j − x] + x)(低い方の支柱に追加)

      • j + x <= N かつ dp[i, j + x] ≠ -1 の場合:dp[i + 1, j] = max(dp[i + 1, j], dp[i, j + x])(高い方の支柱に追加)

      • dp[i, j] ≠ -1 の場合:dp[i + 1, j] = max(dp[i, j], dp[i + 1, j])(ロッドを使用しない)

  • 最後に dp[n, 5000](差が0、つまり両支柱が等しい高さの状態)を返します

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

実装例(C++)

#include <bits/stdc++.h>
using namespace std;
class Solution {
    public:
    int tallestBillboard(vector<int>& rods){
        int sum = 0;
        int n = rods.size();
        int N = 2 * 5000;
        vector<vector<int> > dp(n + 1, vector<int>(N + 1, -1));
        dp[0][5000] = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= N; j++) {
                int x = rods[i];
                if (j - x >= 0 && dp[i][j - x] != -1) {
                    dp[i + 1][j] = max(dp[i + 1][j], dp[i][j - x] +
                    x);
                }
                if (j + x <= N && dp[i][j + x] != -1) {
                    dp[i + 1][j] = max(dp[i + 1][j], dp[i][j + x]);
                }
                if (dp[i][j] != -1) {
                    dp[i + 1][j] = max(dp[i][j], dp[i + 1][j]);
                }
            }
        }
        return dp[n][5000];
    }
};
main(){
    Solution ob;
    vector<int> v = {1,2,2,3,3,3,4};
    cout << (ob.tallestBillboard(v));
}

入力

{1,2,2,3,3,3,4}

出力

9

  1. C++で質素数(Frugal Number)を判定する方法【サンプルコード付き】

    この記事では、正の整数 N が与えられたときに、その数が質素数(Frugal Number)であるかどうかを判定するプログラムを C++ で作成する方法を解説します。 質素数とは? 質素数(FRUGAL NUMBER)とは、その数自身の桁数が、素因数分解による表現の桁数よりも厳密に大きい数のことです。 例:625 の場合 625 を素因数分解すると 54 となります。 625 自身の桁数:3 桁 54 の表現の桁数:2 桁 3 は 2 よりも厳密に大きいため、625 は質素数です。 最初のいくつかの質素数:125、128、243、256、343、512、625 など 問題を理解するための具

  2. C++で五胞体数(ペンタトープ数)を求める方法

    五胞体数とは? 五胞体数(ペンタトープ数)は、パスカルの三角形の第5の対角線上に現れる数列として知られています。この数列を定義するには、パスカルの三角形に少なくとも5つの数が必要となるため、数列の最初の数はパスカルの三角形の第4行である 1 4 6 4 1 から始まります。 本チュートリアルでは、n番目の五胞体数を求める方法を解説します。まずは具体的な例を見てみましょう。 入力 : 1出力 : 1入力 : 4出力 : 35 以下の図から出力を確認できます。 この問題は数列に関するものなので、解法ではまず数列のパターンを見つけることから始めます。 解法のアプローチ このプログラムでは、数列の