Fancy DSA Fancy DSA
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

想的更多,也要想的更远
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • Roadmap 路线
  • 算法简介
  • Sort 排序

  • Search 搜索

  • Recursive 递归

  • Graph 图

  • Tree 树

  • Math 数学

  • Hash 哈希

  • String 字符串

  • BitManipulation 位操纵

  • Backtracking 回溯

  • DynamicProgramming 动态规划

    • ClimbingStairs [爬楼梯]
    • CoinChange [钱币兑换]
    • EditDistance [编辑距离]
    • FibonacciNumber [斐波那契数]
    • FindMonthCalendar [月历]
    • KadaneAlgo [最大连续子数组和之Kadane算法]
    • LevenshteinDistance [莱文斯坦距离]
    • LongestCommonSubsequence [最长公共子序列]
    • LongestIncreasingSubsequence [最长递增子序列]
    • LongestPalindromicSubsequence [最长回文子序列]
    • LongestValidParentheses [最长合法括号]
    • MaxNonAdjacentSum [最大非连接子集和]
    • MaxProductOfThree [最大三数积]
    • MinimumCostPath [最小代价路径]
    • NumberOfSubsetEqualToGivenSum [等和子集]
      • 介绍
      • 实现
      • 参考
    • RodCutting [棒材切割问题]
    • Shuf [随机样本]
    • SieveOfEratosthenes [埃拉托斯特尼筛法]
    • SlidingWindow [滑窗]
    • SudokuSolver [数独]
    • TrappingRainWater [接住雨水]
    • TribonacciNumber [翠波那契数]
    • ZeroOneKnapsack [零一背包]
  • Cache 缓存

  • Array 数组

  • Ciphers 密码学

  • Conversions 转换

  • ProjectEuler 欧拉计划

  • 其他

  • 算法
  • DynamicProgramming 动态规划
jonsam
2022-09-26
目录

NumberOfSubsetEqualToGivenSum [等和子集]

# 介绍

给定一个长度为 N 的数组 arr [] 和一个整数 X,任务是找出总和等于 X 的子集的数量。

一个简单的方法是通过生成所有可能的子集来解决这个问题,然后检查子集是否具有所需的和。这种方法的时间复杂度将是指数级的。

// Naive Approach

#include <stdio.h>

void printBool(int n, int len) {

 while (n) {
  if (n & 1) printf("1 ");
  else printf("0 ");
  n >>= 1;
  len--;
 }

 // This is used for padding zeros
 while (len) {
  printf("0 ");
  len--;
 }
 printf("\n");
}

// Function Prints all the subsets of given set[]
void printSubsetsCount(int set[], int n, int val) {
 int sum; // it stores the current sum
 int count = 0;
 for (int i = 0; i < (1 << n); i++) {
  sum = 0;
  // Print current subset
  for (int j = 0; j < n; j++)

   // (1<<j) is a number with jth bit 1
   // so when we 'and' them with the subset number we get which numbers are present in the subset and which are not
   // Refer: https://www.geeksforgeeks.org/finding-all-subsets-of-a-given-set-in-java/?ref=lbp
   if ((i & (1 << j)) > 0) {
    sum += set[j]; // elements are added one by one of a subset to the sum
   }
  // It checks if the sum is equal to desired sum. If
  // it is true then it prints the elements of the sum
  // to the output
  if (sum == val) {
   /*
   * Uncomment printBool(i,n) to get the boolean
   * representation of the selected elements from
   * set. For this example output of this
   * representation will be 0 1 1 1 0 // 2,3,4
   * makes sum 9 1 0 1 0 1 // 1,3,5 also makes sum
   * 9 0 0 0 1 1 // 4,5 also makes sum 9
   *
   * 'i' is used for 'and' operation so the
   * position of set bits in 'i' will be the
   * selected element. and as we have to give
   * padding with zeros to represent the complete
   * set , so length of the set ('n') is passed to
   * the function.
   * */
   // printBool(i,n);
   count++;
  }
 }
 // it means no subset is found with given sum
 if (count == 0) printf("No subset is found");
 else printf("%d", count);
}

// Driver code
void main() {
 int set[] = { 1, 2, 3, 4, 5 };
 printSubsetsCount(set, 5, 9);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

然而,对于较小的 X 值和数组元素,这个问题可以用动态规划来解决。我们先看一下递归关系。这个方法对所有的整数都有效。

dp[i][C] = dp[i - 1][C - arr[i]] + dp[i - 1][C] 
1

现在我们来了解一下 DP 的状态。这里, dp[i][C] 存储了子数组 arr[i...N-1] 的子集数量,使得它们的总和等于 C。因此,递归是非常琐碎的,因为只有两个选择,即要么考虑子集中的第 i 个元素,要么不考虑。

// Javascript implementation of the approach
const maxN = 20
const maxSum = 50
const minSum = 50
const base = 50
 
// To store the states of DP
const dp = Array.from(Array(maxN), ()=>Array(maxSum+minSum));
const v = Array.from(Array(maxN), ()=>Array(maxSum+minSum));
 
// Function to return the required count
function findCnt(arr, i, required_sum, n) {
  // Base case
  if (i == n) {
    if (required_sum == 0) return 1;
    return 0;
  }
  // If the state has been solved before return the value of the state
  if (v[i][required_sum + base]) return dp[i][required_sum + base];
  // Setting the state as solved
  v[i][required_sum + base] = 1;
  // Recurrence relation
  dp[i][required_sum + base] = findCnt(arr, i + 1, required_sum, n) + findCnt(arr, i + 1, required_sum - arr[i], n);
  return dp[i][required_sum + base];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

使用制表法:

这个方法只对那些包含正数元素的数组有效。在这个方法中,我们使用一个大小为 (arr.size() + 1) * (target + 1) 的整数类型的二维数组。 矩阵的初始化: mat[0][0] = 1 因为如果总和为 0,那么存在总和为 0 的空子集 {}。

if (A[i] > j) DP[i][j] = DP[i-1][j]
else DP[i][j] = DP[i-1][j] + DP[i-1][j-A[i]]
1
2

这意味着,如果当前元素的值大于 "当前总和值",我们将复制以前案例的答案。而如果当前的和值大于 ' 第 1 个 ' 元素,我们将看到之前的任何状态是否已经经历了 sum='j',以及任何之前的状态经历了一个值 'j-A [i]'。

function subsetSum( a,  n, sum) {
  // Initializing the matrix
  var tab = new Array(n + 1);
  for (let i = 0; i< n+1; i++) tab[i] = new Array(sum + 1);
  // Initializing the first value of matrix
  tab[0][0] = 1;
  for (let i = 1; i <= sum; i++) tab[0][i] = 0;

  for (let i = 1; i <= n; i++) {
    for (let j = 0; j <= sum; j++) {
      // if the value is greater than the sum
      if (a[i - 1] > j) tab[i][j] = tab[i - 1][j];
      else tab[i][j] = tab[i - 1][j] + tab[i - 1][j - a[i - 1]];
    }
  }

  return tab[n][sum];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

时间复杂度:O (sumn),其中 sum 是 "目标和","n" 是数组的大小。 辅助空间:O (sumn),因为二维阵列的大小是 sum*n。

空间优化:

我们可以通过关注最后的状态和当前的状态来解决这个问题,所以我们可以在 O (target+1) 的空间复杂度下解决问题。

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

int CountSubsetSum(vector<int>& arr, int val, int n) {
 int count = 0;
 vector<int> PresentState(val + 1, 0),
 LastState(val + 1, 0);
 // consider only last and present state we dont need the (present-2)th state and above and we know for val to be 0 if we dont pick the current index element we can achieve
 PresentState[0] = LastState[0] = 1;
 if (arr[0] <= val) LastState[arr[0]] = 1;
 for (int i = 1; i < n; i++) {
  for (int j = 1; j <= val; j++)
   PresentState[j] = ((j >= arr[i]) ? LastState[j - arr[i]] : 0) + LastState[j];
   // this we will need in the next iteration so just swap current and last state.
   LastState = PresentState;
 }
 // Note after exit from loop we will having a present state which is nothing but the laststate itself;
 return PresentState[val]; // or return CurrentState[val];
}
int main() {
 vector<int> arr = { 3, 3, 3, 3 };
 cout << CountSubsetSum(arr, 6, arr.size());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 实现

# JavaScript

/*
Given an array of non-negative integers and a value sum,
determine the total number of the subset with sum equal to the given sum.
*/
/* Given solution is O(n*sum) Time complexity and O(sum) Space complexity */
function NumberOfSubsetSum (array, sum) {
  const dp = [] // create an dp array where dp[i] denote number of subset with sum equal to i
  for (let i = 1; i <= sum; i++) dp[i] = 0
  dp[0] = 1 // since sum equal to 0 is always possible with no element in subset

  for (let i = 0; i < array.length; i++) {
    for (let j = sum; j >= array[i]; j--) {
      if (j - array[i] >= 0) dp[j] += dp[j - array[i]]
    }
  }
  return dp[sum]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 参考

  • Count of subsets with sum equal to X - GeeksforGeeks (opens new window)
编辑 (opens new window)
上次更新: 2022/10/25, 21:47:42
MinimumCostPath [最小代价路径]
RodCutting [棒材切割问题]

← MinimumCostPath [最小代价路径] RodCutting [棒材切割问题]→

最近更新
01
0-20题解
10-31
02
本章导读
10-31
03
算法与转换:Part1
10-28
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy DSA | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式