📜  子集总和|回溯4

📅  最后修改于: 2021-04-27 20:21:14             🧑  作者: Mango

子集总和问题是找到从给定集合中选择的元素的子集,这些总和加起来等于给定数K。我们正在考虑该集合包含非负值。假定输入集是唯一的(不显示任何重复项)。

子集总和的穷举搜索算法
查找总和为K的子集的一种方法是考虑所有可能的子集。功率集包含从给定集合生成的所有那些子集。这样的电源组的大小为2 N。

子集总和的回溯算法
使用穷举搜索,我们将考虑所有子集,无论它们是否满足给定约束。回溯可用于系统地考虑要选择的元素。
假设给定的4个元素的集合,例如w [1]…w [4] 。树形图可用于设计回溯算法。下面的树形图描述了生成可变大小的元组的方法。

在上面的树中,节点表示函数调用,而分支表示候选元素。根节点包含4个子节点。换句话说,root将集合的每个元素视为不同的分支。下一级子树对应于包括父节点的子集。每个级别的分支表示要考虑的元组元素。例如,如果我们处于级别1,tuple_vector [1]可以取生成的四个分支的任何值。如果我们位于最左侧节点的级别2,则tuple_vector [2]可以取生成的三个分支的任何值,依此类推……

例如,根的最左边的子代会生成所有包含w [1]的子集。类似地,根的第二个子代生成所有包含w [2]并排除w [1]的子集。

当我们沿着树的深度向下移动时,到目前为止我们将添加元素,并且如果添加的总和满足显式约束,我们将继续进一步生成子节点。每当不满足约束条件时,我们都将停止进一步生成该节点的子树,并回溯到上一个节点以探索尚未探索的节点。在许多情况下,它节省了大量的处理时间。

树应该触发实现回溯算法的线索(尝试一下)。它会打印所有总和等于给定数字的子集。我们需要探索沿树的宽度和深度的节点。沿宽度生成节点由循环控制,沿深度的节点使用递归(后顺序遍历)生成。下面给出的伪代码,

if(subset is satisfying the constraint)
    print the subset
    exclude the current element and consider next element
else
    generate the nodes of present level along breadth of tree and
    recur for next levels

以下是使用可变大小元组向量的子集总和的实现。请注意,以下程序探索了与穷举搜索相似的所有可能性。这是为了演示如何使用回溯。请参阅下面的代码以验证如何优化回溯解决方案。

C++
#include 
using namespace std;
#define ARRAYSIZE(a) (sizeof(a)) / (sizeof(a[0]))
#define mx 200
 
static int total_nodes;
 
// Prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        cout << A[i] << " ";
    }
    cout << "\n ";
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
     
    if (target_sum == sum )
    {
         
        // We found subset
        printSubset(t, t_size);
         
        // Exclude previously added item
        // and consider next candidate
        subset_sum(s, t, s_size, t_size - 1,
                   sum - s[ite], ite + 1,
                   target_sum);
        return;
    }
    else
    {
         
        // Generate nodes along the breadth
        for(int i = ite; i < s_size; i++)
        {
            t[t_size] = s[i];
             
            // Consider next level node (along depth)
            subset_sum(s, t, s_size, t_size + 1,
                       sum + s[i], i + 1, target_sum);
        }
    }
}
 
// Wrapper to print subsets that sum to target_sum
// input is weights vector and target_sum
void generateSubsets(int s[], int size,
                     int target_sum)
{
    int *tuplet_vector = new int[mx];
 
    subset_sum(s, tuplet_vector, size,
               0, 0, 0, target_sum);
 
    free(tuplet_vector);
}
 
// Driver Code
int main()
{
    int weights[] = { 10, 7, 5, 18, 12, 20, 15 };
    int size = ARRAYSIZE(weights);
 
    generateSubsets(weights, size, 35);
     
    cout << "Nodes generated " << total_nodes << "\n";
     
    return 0;
}
 
// This code is contributed by samrat2825


C
#include 
#include 
 
#define ARRAYSIZE(a) (sizeof(a))/(sizeof(a[0]))
 
static int total_nodes;
// prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        printf("%*d", 5, A[i]);
    }
 
    printf("\n");
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
    if( target_sum == sum )
    {
        // We found subset
        printSubset(t, t_size);
        // Exclude previously added item and consider next candidate
        subset_sum(s, t, s_size, t_size-1, sum - s[ite], ite + 1, target_sum);
        return;
    }
    else
    {
        // generate nodes along the breadth
        for( int i = ite; i < s_size; i++ )
        {
            t[t_size] = s[i];
            // consider next level node (along depth)
            subset_sum(s, t, s_size, t_size + 1, sum + s[i], i + 1, target_sum);
        }
    }
}
 
// Wrapper to print subsets that sum to target_sum
// input is weights vector and target_sum
void generateSubsets(int s[], int size, int target_sum)
{
    int *tuplet_vector = (int *)malloc(size * sizeof(int));
 
    subset_sum(s, tuplet_vector, size, 0, 0, 0, target_sum);
 
    free(tuplet_vector);
}
 
int main()
{
    int weights[] = {10, 7, 5, 18, 12, 20, 15};
    int size = ARRAYSIZE(weights);
 
    generateSubsets(weights, size, 35);
    printf("Nodes generated %d  \n", total_nodes);
    return 0;
}


C++
#include 
using namespace std;
 
#define ARRAYSIZE(a) (sizeof(a))/(sizeof(a[0]))
static int total_nodes;
 
// prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        cout<<" "<< A[i];
    }
    cout<<"\n";
}
 
// qsort compare function
int comparator(const void *pLhs, const void *pRhs)
{
    int *lhs = (int *)pLhs;
    int *rhs = (int *)pRhs;
    return *lhs > *rhs;
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
 
    if( target_sum == sum )
    {
        // We found sum
        printSubset(t, t_size);
 
        // constraint check
        if( ite + 1 < s_size && sum - s[ite] + s[ite + 1] <= target_sum )
        {
           
            // Exclude previous added item and consider next candidate
            subset_sum(s, t, s_size, t_size - 1, sum - s[ite], ite + 1, target_sum);
        }
        return;
    }
    else
    {
       
        // constraint check
        if( ite < s_size && sum + s[ite] <= target_sum )
        {
           
            // generate nodes along the breadth
            for( int i = ite; i < s_size; i++ )
            {
                t[t_size] = s[i];
                if( sum + s[i] <= target_sum )
                {
                   
                    // consider next level node (along depth)
                    subset_sum(s, t, s_size, t_size + 1, sum + s[i], i + 1, target_sum);
                }
            }
        }
    }
}
 
// Wrapper that prints subsets that sum to target_sum
void generateSubsets(int s[], int size, int target_sum)
{
    int *tuplet_vector = (int *)malloc(size * sizeof(int));
    int total = 0;
 
    // sort the set
    qsort(s, size, sizeof(int), &comparator);
    for( int i = 0; i < size; i++ )
    {
        total += s[i];
    }
    if( s[0] <= target_sum && total >= target_sum )
    {
        subset_sum(s, tuplet_vector, size, 0, 0, 0, target_sum);
    }
    free(tuplet_vector);
}
 
// Driver code
int main()
{
    int weights[] = {15, 22, 14, 26, 32, 9, 16, 8};
    int target = 53;
    int size = ARRAYSIZE(weights);
    generateSubsets(weights, size, target);
    cout << "Nodes generated " << total_nodes;
    return 0;
}
 
//This code is contributed by shivanisinghss2110


C
#include 
#include 
 
#define ARRAYSIZE(a) (sizeof(a))/(sizeof(a[0]))
 
static int total_nodes;
 
// prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        printf("%*d", 5, A[i]);
    }
 
    printf("n");
}
 
// qsort compare function
int comparator(const void *pLhs, const void *pRhs)
{
    int *lhs = (int *)pLhs;
    int *rhs = (int *)pRhs;
 
    return *lhs > *rhs;
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
 
    if( target_sum == sum )
    {
        // We found sum
        printSubset(t, t_size);
 
        // constraint check
        if( ite + 1 < s_size && sum - s[ite] + s[ite+1] <= target_sum )
        {
            // Exclude previous added item and consider next candidate
            subset_sum(s, t, s_size, t_size-1, sum - s[ite], ite + 1, target_sum);
        }
        return;
    }
    else
    {
        // constraint check
        if( ite < s_size && sum + s[ite] <= target_sum )
        {
            // generate nodes along the breadth
            for( int i = ite; i < s_size; i++ )
            {
                t[t_size] = s[i];
 
                if( sum + s[i] <= target_sum )
                {
                    // consider next level node (along depth)
                    subset_sum(s, t, s_size, t_size + 1, sum + s[i], i + 1, target_sum);
                }
            }
        }
    }
}
 
// Wrapper that prints subsets that sum to target_sum
void generateSubsets(int s[], int size, int target_sum)
{
    int *tuplet_vector = (int *)malloc(size * sizeof(int));
 
    int total = 0;
 
    // sort the set
    qsort(s, size, sizeof(int), &comparator);
 
    for( int i = 0; i < size; i++ )
    {
        total += s[i];
    }
 
    if( s[0] <= target_sum && total >= target_sum )
    {
 
        subset_sum(s, tuplet_vector, size, 0, 0, 0, target_sum);
 
    }
 
    free(tuplet_vector);
}
 
int main()
{
    int weights[] = {15, 22, 14, 26, 32, 9, 16, 8};
    int target = 53;
 
    int size = ARRAYSIZE(weights);
 
    generateSubsets(weights, size, target);
 
    printf("Nodes generated %dn", total_nodes);
 
    return 0;
}


当我们结合显式约束和隐式约束时,回溯的力量就会显现,而当这些检查失败时,我们将停止生成节点。我们可以通过加强约束检查和对数据进行预排序来改进上述算法。通过对初始数组进行排序,一旦到目前为止的总和大于目标数,我们就不必考虑数组的其余部分。我们可以回溯并检查其他可能性。

同样,假设该数组已预先排序,我们找到了一个子集。仅当包含下一个节点满足约束时,才能生成不包含当前节点的下一个节点。下面给出的是优化的实现(如果不满足约束条件,它将修剪子树)。

C++

#include 
using namespace std;
 
#define ARRAYSIZE(a) (sizeof(a))/(sizeof(a[0]))
static int total_nodes;
 
// prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        cout<<" "<< A[i];
    }
    cout<<"\n";
}
 
// qsort compare function
int comparator(const void *pLhs, const void *pRhs)
{
    int *lhs = (int *)pLhs;
    int *rhs = (int *)pRhs;
    return *lhs > *rhs;
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
 
    if( target_sum == sum )
    {
        // We found sum
        printSubset(t, t_size);
 
        // constraint check
        if( ite + 1 < s_size && sum - s[ite] + s[ite + 1] <= target_sum )
        {
           
            // Exclude previous added item and consider next candidate
            subset_sum(s, t, s_size, t_size - 1, sum - s[ite], ite + 1, target_sum);
        }
        return;
    }
    else
    {
       
        // constraint check
        if( ite < s_size && sum + s[ite] <= target_sum )
        {
           
            // generate nodes along the breadth
            for( int i = ite; i < s_size; i++ )
            {
                t[t_size] = s[i];
                if( sum + s[i] <= target_sum )
                {
                   
                    // consider next level node (along depth)
                    subset_sum(s, t, s_size, t_size + 1, sum + s[i], i + 1, target_sum);
                }
            }
        }
    }
}
 
// Wrapper that prints subsets that sum to target_sum
void generateSubsets(int s[], int size, int target_sum)
{
    int *tuplet_vector = (int *)malloc(size * sizeof(int));
    int total = 0;
 
    // sort the set
    qsort(s, size, sizeof(int), &comparator);
    for( int i = 0; i < size; i++ )
    {
        total += s[i];
    }
    if( s[0] <= target_sum && total >= target_sum )
    {
        subset_sum(s, tuplet_vector, size, 0, 0, 0, target_sum);
    }
    free(tuplet_vector);
}
 
// Driver code
int main()
{
    int weights[] = {15, 22, 14, 26, 32, 9, 16, 8};
    int target = 53;
    int size = ARRAYSIZE(weights);
    generateSubsets(weights, size, target);
    cout << "Nodes generated " << total_nodes;
    return 0;
}
 
//This code is contributed by shivanisinghss2110

C

#include 
#include 
 
#define ARRAYSIZE(a) (sizeof(a))/(sizeof(a[0]))
 
static int total_nodes;
 
// prints subset found
void printSubset(int A[], int size)
{
    for(int i = 0; i < size; i++)
    {
        printf("%*d", 5, A[i]);
    }
 
    printf("n");
}
 
// qsort compare function
int comparator(const void *pLhs, const void *pRhs)
{
    int *lhs = (int *)pLhs;
    int *rhs = (int *)pRhs;
 
    return *lhs > *rhs;
}
 
// inputs
// s            - set vector
// t            - tuplet vector
// s_size       - set size
// t_size       - tuplet size so far
// sum          - sum so far
// ite          - nodes count
// target_sum   - sum to be found
void subset_sum(int s[], int t[],
                int s_size, int t_size,
                int sum, int ite,
                int const target_sum)
{
    total_nodes++;
 
    if( target_sum == sum )
    {
        // We found sum
        printSubset(t, t_size);
 
        // constraint check
        if( ite + 1 < s_size && sum - s[ite] + s[ite+1] <= target_sum )
        {
            // Exclude previous added item and consider next candidate
            subset_sum(s, t, s_size, t_size-1, sum - s[ite], ite + 1, target_sum);
        }
        return;
    }
    else
    {
        // constraint check
        if( ite < s_size && sum + s[ite] <= target_sum )
        {
            // generate nodes along the breadth
            for( int i = ite; i < s_size; i++ )
            {
                t[t_size] = s[i];
 
                if( sum + s[i] <= target_sum )
                {
                    // consider next level node (along depth)
                    subset_sum(s, t, s_size, t_size + 1, sum + s[i], i + 1, target_sum);
                }
            }
        }
    }
}
 
// Wrapper that prints subsets that sum to target_sum
void generateSubsets(int s[], int size, int target_sum)
{
    int *tuplet_vector = (int *)malloc(size * sizeof(int));
 
    int total = 0;
 
    // sort the set
    qsort(s, size, sizeof(int), &comparator);
 
    for( int i = 0; i < size; i++ )
    {
        total += s[i];
    }
 
    if( s[0] <= target_sum && total >= target_sum )
    {
 
        subset_sum(s, tuplet_vector, size, 0, 0, 0, target_sum);
 
    }
 
    free(tuplet_vector);
}
 
int main()
{
    int weights[] = {15, 22, 14, 26, 32, 9, 16, 8};
    int target = 53;
 
    int size = ARRAYSIZE(weights);
 
    generateSubsets(weights, size, target);
 
    printf("Nodes generated %dn", total_nodes);
 
    return 0;
}

输出:

8 9 14 22n 8 14 15 16n 15 16 22nNodes generated 68

作为另一种方法,我们可以以固定大小的元组类似物生成类似于二进制模式的树。当不满足约束条件时,我们将杀死子树。
– – – Venki