这里写目录标题
求集合中满足一个值的子集和
题目描述
给定一个整数M和n个大于0的整数a1,a2,…,an,要求从n个整数的集合{a1,a2,…,an}中找出所有满足如下条件的子集:子集中各个元素之和等于M。例如,给定M为31,包含4个整数的集合{a1,a2,a3,a4} = {13,24,11,7}。可以看出问题的两个解为子集{24,7}、{13,11,7}。给出具体数据,求出具体的子集。
测试数据
4
31
13 24 11 7
结果
{13,11,7}
{24,7}
代码
#include<iostream>
using namespace std;
int *x,*y;
int size,sum;
void output(){
int count = 0;//计算子集中的个数,这个只是用来控制是否输出逗号的情况
int tempsum = 0;
for(int i = 1;i <= size;i++){
if(y[i] != 0){
tempsum += x[i];//加上被选中的元素
count ++;//统计子集中的个数
}
}
cout<<count<<endl;
if(tempsum == sum){
cout<<"{"; //只是为了好看
for(int j = 1;j <= size;j++){
//循环里边不严格要求格式的话,可以只用一个if语句,判断y[j]!=0后输出x[j]就可以了
if(y[j] != 0 && (count - 1)!= 0){
cout<<x[j]<<",";
count--;
}
else if(y[j] != 0 && (count - 1)== 0)
cout<<x[j]<<"}"<<endl;
}
}
}
void backTrack(int i){
int inhere = 0;//当前被选中元素之和
if(i > size)//如果超过递归层数
output();
else{
for(int j = 1;j >= 0;j--){
y[i] = j;//标志第i个元素被选中
for(int k = 1 ;k <= size;k++){
//本for循环用于计算集合中被选中元素之和
if(y[k] == 1){
inhere += x[k];
}
}
if(inhere < sum){
//如果元素之和还没有期望的sum大,可以继续进行递归
inhere = 0;//这一步解释如下:
//假设当前j = 1,但是发现j = 1时候被选中元素大于期望的sum
//则for循环会置y[i] = 0,如果在这里不将inhere置0的话
//之后的值就会加上原本已经有值的inhere上
backTrack(i + 1);
}
else if(inhere >= sum){
//如果超过或等于期望的sum
if(inhere == sum)//等于的时候可以直接打印了
output();
inhere = 0;//这一步的道理同上边的inhere
}
}
}
}
int main(){
cout<<"请输入集合中有多少个元素:";
cin>>size;
cout<<"请输入目标和:";
cin>>sum;
x = new int[size + 1];
y = new int[size + 1];
cout<<"请输入集合中的元素:";
for(int i = 1;i <= size;i ++){
cin>>x[i];
}
cout<<"能满足条件的子集有:"<<endl;
backTrack(1);
return 0;
}
修改
如果需要输出的是子集中元素的下标,就把output中的if语句改成如下这样:
if(tempsum == sum){
cout<<"{"; //只是为了好看
for(int j = 1;j <= size;j++){
//循环里边不严格要求格式的话,可以只用一个if语句,判断y[j]!=0后输出x[j]就可以了
if(y[j] != 0 && (count - 1)!= 0){
cout<<j<<",";
count--;
}
else if(y[j] != 0 && (count - 1)== 0)
cout<<j<<"}"<<endl;
}
}
框架——回溯法搜索子集树
void backtrack(int k)
{
if(k > n) output(x);
else
for(int i = 0;i <= 1;i++)
{
x[k] = i;
if(constraint(k) && bound(k))//剪枝
backtrack(k + 1);
}
}
旅行商问题
题目
一个推销员要去n个城市推销商品,该推销员从一个城市出发,经过所有城市后回到出发城市。旅行商问题求的是一条行进路线,使得总的行程最短。
测试数据
4
30
6
4
5
10
20
结果
25
2 3 1 4
代码
#include<iostream>
#include<algorithm>
using namespace std;
int minNum = 999;//Min可以适当设置的更大一些,指的是最优路径
int *cities;//指向各个城市的指针
int city = 0;//城市数量
int pathLength[666][666];//各个城市之间的路径长度
int *bestSolute;//走四个城市的最优解
void output(){
cout<<minNum<<endl;
for(int j = 1;j <= city;j++){
cout<<bestSolute[j]<<" ";
}
cout<<bestSolute[1]<<endl;
}
void backtrack(int k){
int sum = 0;
for(int i = k + 1;i <= city;i++){
swap(cities[k],cities[i]);
for(int j = 1;j < city;j++){
sum += pathLength[cities[j]][cities[j + 1]];
if(j == city - 1)
sum += pathLength[cities[j + 1]][cities[1]];
}
if(sum < minNum){
for(int j = 1;j <= city;j++){
//将最优解放置到最优解数组中
bestSolute[j] = cities[j];
}
minNum = sum;//存储这个最小的走法
backtrack(k + 1);
}
swap(cities[k],cities[i]);
}
}
int main(){
//解旅行商问题
cout<<"请输入城市个数:";
cin>>city;
cities = new int[city + 1];
bestSolute = new int[city + 1];
for(int i = 1;i <= city;i ++){
cities[i] = i;
}
cout<<"接下来请输入各城市间出发花费的费用"<<endl;
for(int i = 1;i <= city;i++){
for(int j = 1;j <= city;j++){
if(i == j)
pathLength[i][j] = 0;
else if(pathLength[i][j] == 0){
cout<<"请输入第"<<i<<"个城市到第"<<j<<"个城市的费用:";
cin>>pathLength[i][j];
pathLength[j][i] = pathLength[i][j];
}
}
}
backtrack(1);
output();
return 0;
}
框架——回溯法搜索排列树
void backtrack(int k)
{
if(k > n) output(x);
else
for(int i = k;i <= n;i++)
{
swap(x[k],x[i]);
if(constraint(k) && bound(k))
backtrack(k + 1);
swap(x[k],x[i]);
}
}