给定n个不同的正整数集合w=(w1,w2,…,wn)和一个正数W,要求找出w的子集s,使该子集中所有元素的和为W。
输入格式
第一行输入n和W,第二行依次输入n个数。
输出格式
每行输出一个符合要求的子集。
输入样例
输出样例
解题思路
定义的a[ ]数组,是用来存放输入的数据
定义的x[ ]数组,每一个元素的值是0或1,用来判断当前节点是在子集树中是向左走还是向右走。
通过题目分析可知,本题中应当使用 子集树 作为解空间,使用tw记录当前整数和,使用rw记录余下的整数的和。
扩展节点的约束条件:tw+a[i] <= c i 表示子集树的层数
如果把当前节点加入到当前的整数和中小于等于目标c,那么表示当前节点需要加进来,用x[i]=1表示加进来了,也表示向左走。加进来之后tw=tw+a[i],rw=rw-a[i]
扩展节点的限界函数:tw+rw-a[i]>=c||tw+a[i]>=c
如果a[i]加不进来,那么x[i]=0,代表向右走。
tw+rw-a[i]>=c 表示不加a[i]当前的tw和那些没有被访问的数的和是不是大于等于c,如果不是这样,也就没有向右走的必要了在,再怎么加也加不到目标值c了。一旦决定向右走了,当前的a[i],也就不作数了,必须执行rw=rw-a[i]。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<bits/stdc++.h> using namespace std;
int a[999]; bool x[999]; int tw=0,rw; bool flag=false; int n,c; void backtrack(int i) { if(i>n||flag==true) return ; if(tw==c) { for(int j=0; j<i; j++) if(x[j]==1) cout<<a[j]<<" "; flag=true; return ; } if(tw+a[i]<=c) { x[i]=1; tw=tw+a[i]; rw=rw-a[i]; backtrack(i+1); tw=tw-a[i]; rw=rw+a[i]; } if(tw+rw-a[i]>=c) { x[i]=0; rw=rw-a[i]; backtrack(i+1); rw=rw+a[i]; } return ; } int main() { cin>>n>>c; for(int i=0; i<n; i++) { cin>>a[i]; rw=rw+a[i]; } backtrack(0); if(flag==false) cout<<"No Solution!\n"; return 0; }
|