问题描述:子集和问题的一个实例为<S, t>。其中,S={x, x2, .. xn}是一个正整数的集合,c是一个正整数。子集和问题判定是否存在S的一个子集S1,使得
试设计一个解子集和问题的回溯法。
算法的基本思想
①解向量:n个分量的向量(x1,x2,x3,……xn) xi=0/1,0表示不选取,1表示选取
解空间:子集树,集合中的每个元素选取/不选取
②显式约束函数:子集合中已有元素的和+元素x>c
隐式约束函数:子集合中已有元素的和+剩余未访问的所有元素的和<c
③集合中第一个元素作为根节点,深度优先遍历子集树,将求的的解向量输出到文件。
时间性能:遍历子集树O(2^n)
空间性能:存储路径信息和元素值,O(n)
#include <iostream>
#include <fstream>
using namespace std;
class SubGroup{
public:
void Backtrack(int);
void output();
int rest(int);
SubGroup(int n,int c,int *a){
this->n=n;
this->c=c;
currc=0;
x=new int[n];
w=new int[n];
for(int i=0;i<n;i++)
w[i]=a[i];
found=false;
}
bool found;
private:
int n;
int c;
int currc;//当前累加值
int *x;//路径信息
int *w;// 值
};
ifstream in("input.txt");
ofstream out("output.txt");
void SubGroup::output(){
for(int i=0;i<n;i++)
if(x[i])
out<<w[i]<<' ';
out<<'\n';
}
int SubGroup::rest(int t){
int sum=0;
for(int i=t;i<n;i++)
sum+=w[i];
return sum;
}
void SubGroup::Backtrack(int t){
if(t==n){//到达叶子节点 ,得到了可行解
output();
found=true;
return;
}
//左子树
if(w[t]+currc<=c){
currc+=w[t];
x[t]=1;
Backtrack(t+1);
x[t]=0;
currc-=w[t];
}
//右子树
x[t]=0;
if(currc+rest(t+1)<c) //剪枝
return;//回溯
else
Backtrack(t+1);
}
int main(){
int n,c,i=0;
while(in>>n){
in>>c;
int x[n];
for(int i=0;i<n;i++)
in>>x[i];
SubGroup sb(n,c,x);
sb.Backtrack(0);
if(!sb.found)
out<<"No Solution!"<<'\n';
}
in.close();
out.close();
return 0;
}