当前位置: 首页 > 知识库问答 >
问题:

使用动态规划找到子集和的解决方案

卢鸿彩
2023-03-14

我想做什么

我想找到与目标T求和的数组的子集。我还想使用动态规划方法(以及自下而上的解决方案)来实现这一点。

我目前拥有的

目前,我只找到了一种方法,看看在大小为N的所有子集中,是否至少有一个子集具有所需的和。请参见下面的代码。

public boolean solve(int[] numbers, int target) {

    //Safeguard against invalid parameters
    if ((target < 0) || (sum(numbers) < target)){
        return false;
    }

    boolean [][] table = new boolean [target + 1] [numbers.length + 1]  ;

    for (int i = 0; i <= numbers.length; ++i) {
        table[0][i] = true;
    }


    /* Base cases have been covered. 
     * Now look set subsets [1..n][target] to be true or false.
     * n represents the number of elements from the start that have a subset 
     * that sums to target
     */
    for (int i = 1; i <= target; ++i){
        for (int j = 1; j <= numbers.length; ++j){
            /* Mark index j as one of the numbers in the array
             *  which is part of the solution with the given subtarget */
            table [i][j] = table[i][j-1];
            if (i >= numbers[j-1])
                table[i][j] = table[i][j] || table[i - numbers[j-1]] [j-1];
        }
    }

    return table[target][numbers.length];
}

我被困的地方

现在,我知道是否有解决方案,但我想不出实际输出解决方案的方法。

我不寻找任何人为我提供特定的代码,但伪代码是受欢迎的,就像如何保存解决方案的提示一样。

共有3个答案

全卜霸
2023-03-14

我的解决方案是迭代dp,但只有一个维度:希望它能帮助您。

#include <iostream>
#include <cstring>

using namespace std;

const int maxN=1000;
int memo[maxN];
int pi[maxN];

int main(){
    int a[]={7,8,5,1,4};
    memset(memo,-1,sizeof memo);
    memset(pi,-1,sizeof pi);
    int n;
    cin>>n;
    memo[0]=0;
    pi[0]=0;
    for(int i=0;i<(int)sizeof(a)/4;i++){
        for(int num=n;num>=0;num--){
            if(num-a[i]>=0 and memo[num-a[i]]!=-1 and (memo[num]==-1 or memo[num]>1+memo[num-a[i]])){
                memo[num]=1+memo[num-a[i]]; 
                pi[num]=num-a[i];           
            }
        }
    }   
    int N=n;
    while(N!=0){
        cout<<N-pi[N]<<" ";
        N=pi[N];
    }
    cout<<endl;
    cout<<memo[n]<<endl;
    return 0;
}
后树
2023-03-14

下面是针对子集和问题的两个Java解决方案
首先使用递归方法<其次,使用动态规划方法。

/*
Question: Given a set of non-negative integers, and a value sum, determine if there is a subset of the given set 
with sum equal to given sum.

Examples: set[] = {3, 34, 4, 12, 5, 2}, sum = 9
Output:  True  //There is a subset (4, 5) with sum 9.
Let isSubSetSum(int set[], int n, int sum) be the function to find whether there is a subset of set[] with 
sum equal to sum. n is the number of elements in set[].


*/
package SubsetSumProblem;

import java.util.Scanner;

public class UsingResursiveAndDPApproach {
public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    try{
        System.out.println("Enter the number of elements in the array");
        int n =in.nextInt();
        System.out.println("Enter the elements of the array");
        int[] a=new int[n];
        for(int i=0;i<n;i++)
            a[i]=in.nextInt();
        System.out.println("Enter the sum, which you need to find");
        int sum = in.nextInt();
        System.out.println("Using recursion, the result is: "+usingRecursion(a,a.length,sum));
        System.out.println("Using Dynamic Programming, the result is: "+usingDP(a,sum));
    }
    finally{
        in.close();
    }
}


private static boolean usingRecursion(int[] a,int length, int sum) {

    // 1. Base Cases
    if(sum==0)
        return true;
    if(length==0 && sum!=0)
        return false;

    // 2. To avoid unnecessary steps, we will optimize the recursion method by avoiding 
    //    recursive calls to areas where we are definite that we can SAFELY ignore the case since
    //    the SOLUTION does not exist there.

    // If last element is greater than sum, then ignore it
    if(a[a.length-1]>sum)
        return usingRecursion(a,length-1,sum);

    // 3. This is the recursion step where we will call the method again and again
    /* else, check if sum can be obtained by any of the following
    (a) including the last element
    (b) excluding the last element   */
    return (usingRecursion(a, length-1, sum-a[length-1])|| usingRecursion(a, length-1, sum));

}
/*
Analysis:
    Time Complexity = O(2^n)
    Space Complexity =       // Don't know
*/

private static boolean usingDP(int[] a, int sum) {
    // using boolean matrix for DP
    boolean dp[][] = new boolean[a.length+1][sum+1];  // +1 in row and column


    // if the length of the array is variable (and sum is 0) then fill TRUE, since the SUM=0 
    for(int row=0;row<dp.length;row++){
        dp[row][0] = true;    // NOTE: dp[length=VARIABLE][sum=0], thus we satisfy the condition where length is VARIABLE
                              // and the SUM=0
    }

    // if the SUM is variable and length is 0 then FALSE, since (sum=variable && length=0)
    for(int column=1;column<dp[0].length;column++){
        dp[0][column] = false;  // NOTE: dp[length=0][sum=VARIABLE], thus we satisfy the condition where 
                                // (length=0 && sum=variable)
    }

    for(int i=1;i<dp.length;i++){
        for(int j=1;j<dp[0].length;j++){


            /* Check if sum can be obtained by any of the following
              (a) including the last element
              (b) excluding the last element   */


            // VERY VERY IMP: This is same as "excluding the last element" which is represented in DP 
            dp[i][j] = dp[i-1][j]; // the current position[i][j] would be same as previous position.
                                   // the previous position means that SUM is ACHIEVED OR NOT-ACHIEVED
                                   // int the previous position then it will ofcourse be ACHIEVED or NOT-ACHIEVED
                                   // in the current position.


            // VERY VERY IMP: This is same as "including the last element" which is represented in DP 
            // if the column[ sum is represented in column of the matrix i.e this sum exist] > = sum-a[last_index]
            // then decrease the sum
            if(j>=a[i-1])   // i.e sum >= array[last index element]. If it is true then include this last element by
                            // deducting it from the total sum
                dp[i][j] = dp[i][j] || dp[i-1][j-a[i-1]];  // VERY VERY IMP NOTE: Here dp[i][j] on R.H.S represent
                            // dp[i-1][j] which we have assigned in the previous step

        }
    }
    return dp[a.length][sum];
}
/*
Analysis:
    Time Complexity = O(a.length*sum)
    Space Complexity = O(a.length*sum)
*/
}
鲁烨熠
2023-03-14

您提供的算法可以保持不变,不需要存储DP表以外的任何其他内容。您只需要一个额外的后处理阶段,在该阶段中,您可以“向后”遍历表[][],以获得解决方案集。

回想一下:

您已经计算了表[i][j][code>,它为每个值0存储

考虑与table[i][j](,这是真的)对应的子集S。请注意:

>

  • 只有当表[i-numbers[j]][j-1]为真时,子集S才包含数字

    (证明:递归获取表[i-numbers[j][j-1]的解子集S',并添加numbers[j]

    (证明:假设S中包含数字j,trow,这意味着表i-numbers,矛盾)

    • 如果是,则递归计算数字[n-2]是否在与t-数字[n-1]求和的子集中,
    • 否则递归计算数字【n-2】是否在与t求和的子集中

  •  类似资料:
    • 我有一个子集问题的工作代码,如果它发现一个子集等于所需的目标,它可以打印数字。 > 我想打印给定目标的所有可能的子集,我不知道要为此更改什么。 我如何让它对负数起作用?

    • 我正在练习动态编程,我正在努力调试我的代码。这个想法是在给定一组数字的情况下找出求和是否可能。这是我的代码: 以下是输出: 据我所知,在我的fe语句中,算法应该向上1行,然后查看x和y的差异并检查该槽是否可能。例如,在最明显的情况下,最后一行的最后一个元素。那将是10(y)-11(x),它应该一直回到它上面一行的索引1,我们知道这是True。不完全确定我做错了什么,如果能帮助理解这一点,将不胜感激

    • 给定一个数组,是否可以从起始索引开始选择一组整数,这样该组就与给定的目标相加?但是,附加的限制是必须选择所有的6。 groupSum6(0,[5,6,2],8)true groupSum6(0,[5,6,2],9)false groupSum6(0,[5,6,2],7)false 只是想弄清楚我错在哪里。声明nums[start]==6的特殊情况是不是错误的方法?

    • 我正在为动态编程编写一些复习材料。我需要提出如何划分子问题,计算出基本情况,并提出递归公式。 给定 n 个正整数 a1,a2,...,an、一个数字 k 和一个目标 W,我们希望选择一个子集 T,其总和恰好是 k 个元素,其总和最接近 W。每个元素只能选择一次。定义一个具有 3 个参数的子问题(即 C[x,y,z] = ...)。 我只处理过几个动态编程示例,从未处理过定义子问题时需要3个参数的示

    • 我无法弄清楚重叠子问题的DP第一属性在哪里适合子集和问题。然而,我理解最优子结构部分。在执行包含和排除元素的递归解决方案时,问题在哪里重叠 是不是因为这是一个NP问题,所以没有DP的两个性质 问题的链接是http://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/ 有人能帮助我理解这一点吗。

    • 问题链接如下: https://www.geeksforgeeks.org/dynamic-programming-subset-sum-problem/ 我没有看到重叠的子问题属性在问题中得到满足,至少在输入情况下是如此。例如,在下面的链接中,递归树没有任何重叠的子问题 http://www.zrzahid.com/subset-sum-problem-dynamic-programming/