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

C++で積がK未満となる部分列の個数を求める方法

このチュートリアルでは、積がK未満となる部分列(サブシーケンス)の個数を求めるプログラムについて解説します。

問題設定は次のとおりです。非負の整数からなる配列と値kが与えられたとき、配列の要素を選んで構成できる部分列のうち、要素の積がk未満になるものの総数を求めます。

動的計画法によるアプローチ

この問題は動的計画法(DP)を使うことで効率的に解けます。ここでは、dp[i][j] を「先頭からj個の要素の中から選んだ部分列のうち、積がi未満になるものの個数」と定義します。

遷移は次のように考えます。まず、j番目の要素 arr[j-1] を部分列に含めない場合の数は dp[i][j-1] です。次に、含める場合は、その要素を末尾に追加した形の部分列が新たに数え上がるため、dp[i / arr[j-1]][j-1] + 1 を加算します。なお、ゼロ除算を避けるため、arr[j-1] が0より大きい場合のみこの遷移を行います。

サンプルコード

#include <bits/stdc++.h>
using namespace std;

// 積がk未満の部分列を数える関数
int count_sub(vector<int> &arr, int k){
    int n = arr.size();
    int dp[k + 1][n + 1];
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= k; i++) {
        for (int j = 1; j <= n; j++) {
            // arr[j-1] を含まない場合
            dp[i][j] = dp[i][j - 1];
            // arr[j-1] を含む場合
            if (arr[j - 1] <= i && arr[j - 1] > 0)
                dp[i][j] += dp[i / arr[j - 1]][j - 1] + 1;
        }
    }
    return dp[k][n];
}

int main(){
    vector<int> A;
    A.push_back(1);
    A.push_back(2);
    A.push_back(3);
    A.push_back(4);
    int k = 10;
    cout << count_sub(A, k) << endl;
}

実行結果

11

出力の解説

上記の例では、配列 {1, 2, 3, 4} と k = 10 が与えられています。積が10未満となる部分列は、次の11個です。

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

例えば {3,4} の積は12でk以上となるため除外され、{1,2,3} の積は6、{1,2,4} の積は8なので条件を満たします。DPテーブルの最終的な答え dp[10][4] が11となり、正しく個数が求まっていることが確認できます。

計算量

時間計算量・空間計算量はいずれも O(k × n) です。kが非常に大きい場合はメモリ使用量に注意が必要ですが、典型的な制約条件下では全部分列を列挙する指数時間のアプローチよりはるかに効率的です。

  1. 【C++】配列内のすべての素数の積を求める方法

    整数型配列 arr[] が与えられたとき、その配列に含まれるすべての素数を見つけ出し、それらの積を計算するのが本記事のテーマです。素数とは、1とその数自身でしか割り切れない正の整数のことです。たとえば、2、3、5、7、11などが素数に該当します。それでは、次の配列を例に解を求めてみましょう。入力: arr[] = { 11, 20, 31, 4, 5, 6, 70 }出力: 1705説明: 配列内の素数は 11、31、5 の3つであり、その積は 11 × 31 × 5 = 1705 となります。入力: arr[] = { 1, 2, 3, 4, 5, 6, 7 }出力: 210説明: 配列内の

  2. C++で最小ヒープから値x未満のすべてのノードを出力する方法

    この問題では、最小ヒープ(Min Heap)と値xが与えられ、xより小さい値を持つすべてのノードを出力することが求められます。最小ヒープとは、すべての親ノードがその子ノードの値以下となる特殊な二分木です。この性質により、根(ルート)には常にヒープ内の最小値が格納されます。具体例を使って問題を理解しましょう。X = 45出力 − 2 4 7 10 17 22 33 34この問題を解くには、最小ヒープ全体を先行順トラバーサル(前順走査)で探索し、与えられた値xより小さい値を持つノードのみを出力します。アルゴリズムのポイント最小ヒープでは親ノードの値が必ず子ノード以下であるため、あるノードの値がx以