1.问题描述:
设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法。输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值。接下来的1行中,有n个正整数,表示集合S中的元素。 是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。将子集和问题的解输出。当问题无解时,输出“No Solution!”。
2.解决问题的思路:
A.用回溯法解决问题,首先我们需要判断应当使用子集树还是排列树。当然对于这个问题,应该使用子集树。
B.然后我们就可以套入子集数的模版
C.当然我们也需要根据实际情况进行相应的调整。用适当的Constraint()和Bound()函数进行递归条件的控制。
3.代码示例:
#include<stdio.h>
# define M 100
int n;//正整数集合成员数量
int all;// 正整数子集的和
int arr[M];//正整数集合
int x[M]; //记录子集成员
int mark=0;
int Constrain(int t){
int i;
int sum=0;
for(i=0;i<=t;i++) {//子集求和
if(x[i]==1) sum+=arr[i];
}
if(sum==all){
printf("找到最优解");
for(i=0;i<=t;i++) { //把最优解打印出来
if(x[i]==1) printf("%d ",arr[i]);
}
mark=1; //只要找到最优解,那么问题就结束
}
}
int Bound(int t){//检测有没有到达叶子节点
if(t<n)
return 1;
else return 0;
}
int Backtrack(int t){//典型的子集树,不再解释
int i;
if(t<n){
for(i=0;i<=1;i++){
x[t]=i;
if(Constrain(t)&&Bound(t)){
if(mark==1)return 0;
Backtrack(t+1);
}
}
}
}
int main(){
int i;
scanf("%d %d",&n,&all);
for(i=0;i<n;i++) {//给正整数集合赋值
scanf("%d",&arr[i]);
}
for(i=0;i<n;i++) {//记录子集成员,一开始为空,也就是标记集合为全为0
x[i]=0;
}
Backtrack( 0);
}
4.需要注意的一点是,对于子集和问题,找到问题的一个解即可。因此找到解程序就结束。如果是其他问题,可以根据情况作调整。