子集和问题

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
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值