簡體   English   中英

計算總和等於 k 的子集數

[英]Count number of subsets with sum equal to k

給定一個數組,我們需要找出總和正好等於給定 integer k 的子集的數量。 請為這個問題提出一個最佳算法。 這里不需要實際的子集,只需要計數就可以了。

該數組由整數組成,可以是負數也可以是非負數。

示例:Array -> {1,4,-1,10,5} abs sum->9 對於 {4,5} 和 {-1,10},答案應該是 2

這是子集求和問題的變體,它是NP-Hard問題,因此沒有已知的多項式解 (實際上,子集求和問題說很難找到是否有一個子集求和到給定的總和)。

解決它的可能方法是蠻力(檢查所有可能的子集),或者如果集合包含相對較小的整數,則可以使用偽多項式動態規划技術:

f(i,0) = 1    (i >= 0) //succesful base clause
f(0,j) = 0    (j != 0) //non succesful base clause
f(i,j) = f(i-1,j) + f(i-1,j-arr[i])  //step

將動態規划應用於上述遞歸公式可為您提供O(k*n)時間和空間解決方案。

使用f(n,k)調用 [假設數組的索引為 1]。

以下是記憶化的動態編程代碼,用於打印具有給定總和的子集數量。 DP 的重復值存儲在“tmp”數組中。 要獲得 DP 解決方案,首先總是從問題的遞歸解決方案開始,然后將重復值存儲在 tmp 數組中以得出記憶化的解決方案。

#include <bits/stdc++.h>

using namespace std; 

int tmp[1001][1001];  


int subset_count(int* arr, int sum, int n) 
{ ` if(sum==0)
        return 1;
    if(n==0)
        return 0;
    if(tmp[n][sum]!=-1)
        return tmp[n][sum];
    else{
        if(arr[n-1]>sum)
            return tmp[n][sum]=subset_count(arr,sum, n-1);
        else{
            return tmp[n][required_sum]=subset_count(arr,sum, n- 1)+subset_count(arr,sum-arr[n-1], n-1);`
        }
    }
} 

// Driver code 

int main() 
{ ` memset(tmp,-1,sizeof(tmp));
    int arr[] = { 2, 3, 5, 6, 8, 10 }; 
    int n = sizeof(arr) / sizeof(int); 
    int sum = 10; `


    cout << subset_count(arr,sum, n); 

    return 0; 
}

這是遞歸解決方案。 它的時間復雜度為 O(2^n) 使用動態規划將時間復雜度提高為二次 O(n^2)

def count_of_subset(arr,sum,n,count):
    if sum==0:
        count+=1
        return count
    if n==0 and sum!=0:
        count+=0
        return count
    if arr[n-1]<=sum:
        count=count_of_subset(arr,sum-arr[n-1],n-1,count)
        count=count_of_subset(arr,sum,n-1,count)
        return count
    else:
        count=count_of_subset(arr,sum,n-1,count)
        return count
int numSubseq(vector<int>& nums, int target) {
    int size = nums.size();
    int T[size+1][target+1];
    for(int i=0;i<=size;i++){
        for(int j=0;j<=target;j++){
            if(i==0 && j!=0)
                T[i][j]=0;
            else if(j==0)
                T[i][j] = 1;
        }
    }
    for(int i=1;i<=size;i++){
        for(int j=1;j<=target;j++){
            if(nums[i-1] <= j)
                T[i][j] = T[i-1][j] + T[i-1][j-nums[i-1]];
            else
                T[i][j] = T[i-1][j];
        }
    }
    return T[size][target];
}

盡管如果約束條件為:1<=v[i]<=1000,上述基本情況將正常工作

但請考慮:約束條件:0<=v[i]<=1000

上面的基本情況會給出錯誤的答案,考慮一個測試用例:v = [0,0,1] 和 k = 1,根據基本情況,output 將為“1”。

但正確答案是 3:{0,1}{0,0,1}{1}

為了避免這種情況,我們可以 go deep 而不是返回 0,並通過以下方式修復它

C++:

if(ind==0)
{
if(v[0]==target and target==0)return 2;  
if(v[0]==target || target==0)return 1; 
return 0 ;
}

該解決方案的答案之一是生成 N 的冪集,其中 N 是數組的大小,將等於 2^n。 對於 0 到 2^N-1 之間的每個數字,檢查其二進制表示,並包括該位位於設置位置的數組中的所有值,即 1。 檢查您包含的所有值的總和是否等於所需值。 這可能不是最有效的解決方案,但由於這是一個 NP 難題,因此該問題不存在多項式時間解決方案。

暫無
暫無

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

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