SCAU OJ题 8603 子集和问题(优先做)

8603 子集和问题(优先做)
时间限制:1000MS 内存限制:1000K
提交次数:795 通过次数:262

题型: 编程题 语言: G++;GCC;VC

Description
S是一个整数集合,S={x1,x2,…,xn},c是一个整数。这里集合元素xi(1<=i<=n)和c都是整数,可能为负。

子集和问题就是:判断是否存在S的一个子集S1,使得:

对S集合子集树采用深度优先的顺序进行搜索,子集树从上到下每层标示着S集合中每个从左到右元素“选”或者“不选”(左1右0)。

试着用回溯算法设计解子集和问题。

输入格式
第一行2个数:正整数n和整数c。n表示S集合的大小(n<=100),c是子集和的目标值。
接下来一行中,有n个整数,表示集合S中的元素。

输出格式
将子集和问题的解输出,当无解时,输出"No Solution"(注意No Solution的大小写,空格,无标点)。
注意:依据S集合元素从左到右依次来画子集树,因此子集树唯一。
若存在多种子集和问题的解时,只输出在这个唯一的子集树按深度优先方向遇到的第一个解,这样保证解的唯一性,利于评判。
如:5 10
2 2 6 3 3
这里,2+2+6=10,2+2+3+3=10,但只输出2 2 6

如:5 10
2 2 3 3 6
只输出2 2 3 3

又如:5 -30
2 -2 6 -30 -3
只输出2 -2 -30

输入样例
5 10
2 2 6 5 4

输出样例
2 2 6

提示
解空间树是“子集树”。
回溯法搜索,由于是深度优先的找,找到就退出。
参考书上的“装载问题”和书上的“0-1背包问题”来写,因为都是搜索子集树。
但此题无法有很好的剪枝优化,要求只输出“在子集树中按深度优先方向遇到的第一个解”,因此找到一个符合
条件的叶子就退出。

有个问题就是,找到第一个解就退出,怎么退出呢?
若你是用循环的迭代回溯实现,由于循环是很好退出的,要退出搜索,就break出来即可,很简单。但若是递归实
现的回溯过程,因是递归函数的逐层调用,现在要退出整个递归过程,如何一层一层退出被递归调用的函数?

解决办法就是加一个全局的标志found,found初始化为false。
当found标志为false时,就可递归深入下去,但当标志为true时,直接退出本次函数的过程。found标志需要
在找到第一个叶子时更新为true。

#include <iostream>
#include <algorithm>
#include <math.h>

using namespace std;
void Backtrack(int i);
int n=0,c=0,sum=0;
int a[101]={0};
bool record[101]={false};//置为false
bool found= false;
int main()
{
    cin>>n>>c;
    for (int i = 1; i <=n ; i++) {
        cin>>a[i];
    }
    Backtrack(1);
    for (int i = 1; i <=n ; i++) {
        if(record[i]== true)
            cout<<a[i]<<" ";
    }
    if(found== false)
        cout<<"No Solution";
}
void Backtrack(int i){
    if(i>n){//一个分支走完
        if(sum==c)//找到解,标志位改变
            found=true;
        return;
    }
    if (found == false) {
        sum+=a[i];
        record[i]=true;
        Backtrack(i + 1);
        if (found == true)
            return;
        sum-=a[i];
        record[i]= false;
        Backtrack(i+1);
    }
}


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值