簡體   English   中英

計算組合的有效算法,數組的重復加起來等於給定的總和

[英]Efficient algorithm to compute combinations with repetitions of an array adding up to given sum

因此,在個人 C++ 項目中,我遇到了一個問題。 我將其改寫如下:

給定一個包含n 個元素的數組(例如 [1, 3, 5],其中 n = 3 個元素),其中第i個 position 處的數字表示第i個索引處的數字可以取多少個可能值(例如,這里的第一個元素可以取 1 個值,即 0;第二個元素可以取 0,1,2 中的 3 個值;第三個元素可以取 0,1,2,3,4 中的 5 個值)。

我需要列出所有可能的 arrays 長度n總和小於或等於給定數字k 這是一個例子:

輸入 1

輸入數組 = [2,2]; k = 2

Output 1

[0,0], [0,1], [1,0], [1,1]

另外,例如:

輸入 2

輸入數組 = [2,2]; k = 1

Output 2 :

[0,0], [0,1], [1,0]

問題

我編寫了一個簡單的遞歸和一個簡單的迭代解決方案,它枚舉了所有 arrays 並且只保留總和小於 k 的那些 這些問題在於,對於n 很大且 k = 1的情況,我的代碼需要很長時間才能運行,因為它枚舉了所有情況並保留了一些情況。

我看不到任何重疊的子問題,所以我覺得 DP 和 memoization 不適用。 我怎樣才能為此工作編寫所需的 C++ 代碼?

這是我的迭代版本代碼:

// enumerates all arrays which sum up to k

vector<vector<int> > count_all_arrays(vector<int> input_array, int k){

    vector<vector<int> > arr;
    int n = (int)input_array.size();

    // make auxilliary array with elements

    for(int i = 0; i < n; i++){
        vector<int> temp(input_array[i]);
        std::iota(temp.begin(), temp.end(), 0);
        arr.push_back(temp);
    }

    // computes combinations

    vector<int> temp(n);
    vector<vector<int> > answers;
    vector<int> indices(n, 0);
    int next;

    while(1){ 
        temp.clear();
        for (int i = 0; i < n; i++) 
            temp.push_back(arr[i][indices[i]]);  
        long long int total = accumulate(temp.begin(), temp.end(), 0);
        if(total <= k)
            answers.push_back(temp);
        next = n - 1; 
        while (next >= 0 &&  
              (indices[next] + 1 >= (int)arr[next].size())) 
            next--; 
        if (next < 0) 
            break; 
        indices[next]++; 
        for (int i = next + 1; i < n; i++) 
            indices[i] = 0; 
    }
    return answers;
}

這是一個非常簡單的遞歸任務:

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

int arr[] = {2, 2};
int n = 2;
int k = 2;

void gen(int pos, int sum, string s){
    if(pos == n){
        cout<<"["<<s<<" ]"<<endl;
        return;
    }
    for(int i = 0; i < arr[pos]; i++){
        if(sum + i > k) return;
        gen(pos + 1, sum + i, s + " " + to_string(i));
    }
}

int main(){
    gen(0, 0, "");
    return 0;
}

只需為數組的每個插槽生成所有可能性,對於每個選擇,將總和用於評估下一個插槽。

n很大且k = 1時,很自然需要 O(n),因為您將擁有:

[0, 0, 0, ..., 0, 0, 1]
[0, 0, 0, ..., 0, 1, 0]
[0, 0, 0, ..., 1, 0, 0]
          ...
[0, 0, 1, ..., 0, 0, 0]
[0, 1, 0, ..., 0, 0, 0]
[1, 0, 0, ..., 0, 0, 0]

您應該使用 dp 使其在任何情況下都快速運行。 使用 dp[i][j] 表示您使用第一個 j 元素創建小於或等於 i 的總和的次數。

dp[i][j] = dp[

for (int l = 0; l <= i; l++) 
    dp[i][j] += dp[l][j] + min(i-l+1, input[j])

結果是 dp[k,n]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM