题目
子集和问题的一个实例为<S,c>。其中,S={x1,x2,…,xn}是一个正整数的集合,c是一个正整数。子集和问题判定是否存在S的一个子集S1,使得子集S1和等于c。
对于给定的正整数的集合S={x1,x2,…,xn}和正整数c,编程计算S 的一个子集S1,使得子集S1和等于c。
输入
第1行有2个正整数n和c,n表示S的个数,c是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。
输出
输出一个解即可,如果没有解,输出“No Solution!”。
样例输入
5 10
2 2 6 5 4
样例输出
2 2 6
分析
使用深度优先(dfs)可以很容易解决。设立一个flag数组用来标记第i个数字是否已经被引用,设立一个cnt来记录是都找到解,如果找到结束搜索。写dfs函数时间传入两个值,一个来表示是否已经遍历所有的项,一个来记录数字和。分左右两枝来进行搜索。
代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,c;
ll a[1000005];
bool flag[1000005];//定义bool类型的数组,标记数字的使用与否;
ll cnt=0,sum=0;
void dfs(ll t,ll w)
{
if(cnt==1)//如果已经找到一组,返回空,结束深搜;
return;
// 深度搜索的终止条件;
if(t==n+1)
{
if(w==c)//搜索到符合条件的值,进行输出这一组解;
{
for(ll i=1;i<=n;i++)
{
if(flag[i]==1)
{
printf("%lld ",a[i]);
}
}
cnt++;//找到一组cnt+1作为标记;
}
return;
}
if(w>c)//如果已经使用的数字和大于所需,在往下搜索就不可能满足要求;
return;//因为往下已经不可能满足要求了,所以进行截枝,节省时间;
//左枝,即使用这个数;
flag[t]=1;//将这个数标记为 1,即使用该数;
dfs(t+1,w+a[t]);//递归深搜
//右枝,即不使用这个数;
flag[t]=0;//将这一数字标记为0,即没有使用这一数字;
dfs(t+1,w);//递归深搜
}
int main()
{
cin>>n>>c;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum+=a[i];//求数字的和;
}
if(sum<c)//判断所有数字的和是不是大于所需求的数字的和,避免不必要的深搜;
printf("No Solution!");
else
{
dfs(1,0);//深搜开始
if(cnt==0)//如果深搜没有找到合适的解,输出没有解;
printf("No Solution!");
}
return 0;
}