Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 理解子集和_C++_Algorithm_Backtracking_Subset Sum - Fatal编程技术网

C++ 理解子集和

C++ 理解子集和,c++,algorithm,backtracking,subset-sum,C++,Algorithm,Backtracking,Subset Sum,我刚在大学开始学习回溯算法。不知何故,我已经设法为子集和问题编制了一个程序。工作正常,但后来我发现我的程序并没有给出所有可能的组合 例如:一个目标和可能有100个组合,但我的程序只给出30个。 这是代码。如果有人能指出我的错误,那将是一个很大的帮助 int tot=0;//tot is the total sum of all the numbers in the set. int prob[500], d, s[100], top = -1, n; // n = number of eleme

我刚在大学开始学习
回溯算法。不知何故,我已经设法为子集和问题编制了一个程序。工作正常,但后来我发现我的程序并没有给出所有可能的组合

例如:一个目标和可能有100个组合,但我的程序只给出30个。 这是代码。如果有人能指出我的错误,那将是一个很大的帮助

int tot=0;//tot is the total sum of all the numbers in the set.
int prob[500], d, s[100], top = -1, n; // n = number of elements in the set. prob[i] is the array with the set.
void subset()
{
    int i=0,sum=0; //sum - being updated at every iteration and check if it matches 'd'
    while(i<n)
    {
        if((sum+prob[i] <= d)&&(prob[i] <= d)) 
        {
            s[++top] = i;
            sum+=prob[i];
        }
        if(sum == d) // d is the target sum 
        {
            show(); // this function just displays the integer array 's'
            top = -1; // top points to the recent number added to the int array 's'
            i = s[top+1];
            sum = 0;
        }
        i++;
        while(i == n && top!=-1)
        {
            sum-=prob[s[top]];
            i = s[top--]+1;
        }
    }
}

int main()
{
    cout<<"Enter number of elements : ";cin>>n;
    cout<<"Enter required sum : ";cin>>d;
    cout<<"Enter SET :\n";
    for(int i=0;i<n;i++)
    {
        cin>>prob[i];
        tot+=prob[i];
    }
    if(d <= tot)
    {
        subset();
    }
    return 0;
}
虽然4,8也是一个解决方案,但我的程序没有显示它。 更糟糕的是,输入的数量为100或更多。将有至少10000个组合,但我的程序显示100个

我试图遵循的逻辑是:

  • 只要 子集的和仍然小于或等于目标和
  • 如果在子集和上加上一个特定的数字 比目标大,它不接受
  • 一旦到达终点 如果未找到答案,则删除最多 最近从集合中取出数字并开始查看数字 在最近删除的编号后的位置。 (因为我在数组“s”中存储的是 从主集合中选择的数字)

  • 您将要找到的解决方案取决于集合中条目的顺序,这是由于您在步骤1中使用了“只要”子句

    如果你接受条目,只要它们不能让你超过目标,那么一旦你接受了例如“4”和“2”,那么“8”将让你超过目标,因此只要“2”在“8”之前在你的集合中,你就永远不会得到“4”和“8”的子集


    您应该添加跳过添加条目的可能性(或者将其添加到一个子集,但不添加到另一个子集),或者更改集合的顺序并重新检查它。

    可能是无堆栈解决方案,但实现回溯算法的通常(通常也是最简单的!)方法是通过递归,例如:

    int i = 0, n;    // i needs to be visible to show()
    int s[100];
    
    // Considering only the subset of prob[] values whose indexes are >= start,
    // print all subsets that sum to total.
    void new_subsets(int start, int total) {
        if (total == 0) show();    // total == 0 means we already have a solution
    
        // Look for the next number that could fit
        while (start < n && prob[start] > total) {
            ++start;
        }
    
        if (start < n) {
            // We found a number, prob[start], that can be added without overflow.
            // Try including it by solving the subproblem that results.
            s[i++] = start;
            new_subsets(start + 1, total - prob[start]);
            i--;
    
            // Now try excluding it by solving the subproblem that results.
            new_subsets(start + 1, total);
        }
    }
    
    int i=0,n;//我需要可见才能显示()
    int s[100];
    //仅考虑索引为>=start的prob[]值的子集,
    //打印总计的所有子集。
    作废新的_子集(整数开始,整数总计){
    如果(total==0)show();//total==0表示我们已经有了解决方案
    //寻找下一个合适的号码
    while(start总计){
    ++开始;
    }
    如果(开始
    然后,您可以使用
    new_子集(0,d)从
    main()
    调用此函数。一开始,递归可能很难理解,但重要的是要了解它——如果上面的内容没有任何意义,请尝试更简单的问题(例如,递归生成斐波那契数)

    与您给出的解决方案相反,我看到的一个问题是,一旦找到解决方案,您就将其删除,并开始从该解决方案中包含的第一个数字右侧的数字开始寻找新的解决方案(
    top=-1;I=s[top+1];
    意味着
    I=s[0]
    ,然后是后续的
    i++;
    )。这将丢失以相同的第一个数字开头的解决方案。如果(sum==d){show();}
    ,则只需执行
    ,以确保所有代码都已完成


    我最初发现您的内部
    循环非常混乱,但我认为它实际上做了正确的事情:一旦
    I
    到达数组末尾,它将删除添加到部分解的最后一个数字,如果这个数字是数组中的最后一个数字,它将再次循环以从部分解决方案中删除倒数第二个数字。它不会循环超过两次,因为部分解中包含的数字都位于不同的位置。

    我没有详细分析算法,但让我吃惊的是,您的算法没有考虑到这样一种可能性,即在有一个以数字X开头的解之后,从这个数字开始,可能有多种解决方案


    第一个改进是避免在打印解决方案后重置堆栈
    s
    和运行总和。

    如果您的变量有更多描述性名称(这对您的编程生涯通常很有用),或者至少如果您告诉我们每个变量的含义、声明方式,初始化等。您可以添加一些示例输入、您期望的输出以及输出是什么。这段代码是如何给出任何东西的,或者它从哪里得到输入的,这一点都不清楚。很抱歉这么含糊。这是我第一次在网上发布代码。第二次
    while()
    循环看起来很奇怪。总的来说,我很难理解你的算法逻辑。你能用一些自然语言的词语表达清楚吗?好的,我刚刚编辑了我的帖子,并添加了我试图使用的逻辑。第二个while循环将删除最近添加到子集中的元素。这几乎是正确的——如果事实上无法从4和2生成解决方案,那么内部
    while
    循环将删除2并开始在其右侧搜索。@j_random_hacker关键是,如果可以使用4和2生成解决方案,它仍然应该在没有2的情况下尝试,找到有4而没有2的组合。这是正确的,但是你在回答中说了一些稍微不同的东西。“只要“2”在“8”之前在你的集合中,你就永远不会得到一个带有“4”和“8”的子集”不是真的——如果没有办法用4和2来解决问题,你可以得到一个带有4和8的子集。@j_random_hacker我指的是他的示例序列4 3 2 6 8。。。在这个序列中,一个组合开始了
    int i = 0, n;    // i needs to be visible to show()
    int s[100];
    
    // Considering only the subset of prob[] values whose indexes are >= start,
    // print all subsets that sum to total.
    void new_subsets(int start, int total) {
        if (total == 0) show();    // total == 0 means we already have a solution
    
        // Look for the next number that could fit
        while (start < n && prob[start] > total) {
            ++start;
        }
    
        if (start < n) {
            // We found a number, prob[start], that can be added without overflow.
            // Try including it by solving the subproblem that results.
            s[i++] = start;
            new_subsets(start + 1, total - prob[start]);
            i--;
    
            // Now try excluding it by solving the subproblem that results.
            new_subsets(start + 1, total);
        }
    }