8603 子集和问题
时间限制:1000MS 内存限制:1000K
提交次数:0 通过次数:0
题型: 编程题 语言: 无限制
8603 子集和问题
8603 子集和问题
时间限制:1000MS 内存限制:1000K
提交次数:0 通过次数:0
题型: 编程题 语言: 无限制
Description
S是一个整数集合,S={x1,x2,...,xn},c是一个整数。这里集合元素xi(1<=i<=n)和c都是整数,可能为负。子集和问题就是:判断是否存在S的一个子集S1,使得:
对S集合子集树采用深度优先的顺序进行搜索,子集树从上到下每层标示着S集合中每个从左到右元素“选”或者“不选”(左1右0)。
试着用回溯算法设计解子集和问题。

Input
第一行2个数:正整数n和整数c。n表示S集合的大小,c是子集和的目标值,接下来一行中,有n个整数,表示集合S中的元素。
Output
将子集和问题的解输出,当无解时,输出"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
Sample Input
5 10 2 2 6 5 4
Sample Output
2 2 6
Hint
本题是简单的回溯法可解问题,因为输入数有正有负,故没有较好的剪枝函数,在下面的代码中,只是简单的全部遍历,遇到第一个最优解便退出递归并输出。
Code
#include<iostream>
#include<math.h>
using namespace::std;
void Subset_sum(int i,int n,int c,int *ary,int &cs,int *x,int &leaf){
if(i>n){
leaf++; //叶子节点+1
if(cs==c){
for(int i=1;i<=n;++i)
if(x[i]==1)
cout<<ary[i]<<" "; //输出第一个解
cout<<endl;
exit (0);
}//if
if(leaf==pow(2,n))
cout<<"No Solution"<<endl;
return;
}
x[i]=1; //遍历左子树
cs+=ary[i];
Subset_sum(i+1,n,c,ary,cs,x,leaf);
cs-=ary[i];
x[i]=0; //遍历右子树
Subset_sum(i+1,n,c,ary,cs,x,leaf);
}
int main(){
int n,c;
int leaf=0; //记录已遍历过的叶子结点,作为有无解的判定(如果最后返回时已经遍历的叶子节点数为pow(2,n),这说明无解)
int cs=0;
cin>>n;
cin>>c;
int *x=new int[n+1]; //由于只输出遇到的第一个解,所以此处只设置一个解向量
int *set=new int[n+1];
for(int i=1;i<=n;++i)
cin>>set[i];
Subset_sum(1,n,c,set,cs,x,leaf);
}
/*
Sample Input
5 10
2 2 6 5 4
Sample Output
2 2 6
Sample Input
5 10
2 2 1 1 1
Sample Output
No Solution
*/