子集和问题(回溯法)

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.需要注意的一点是,对于子集和问题,找到问题的一个解即可。因此找到解程序就结束。如果是其他问题,可以根据情况作调整。

    原文作者:回溯法
    原文地址: https://blog.csdn.net/wuthering_wind/article/details/80473686
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞