【计算机算法】回溯——子集和问题、旅行商问题

求集合中满足一个值的子集和

题目描述

给定一个整数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]);
		}
}

猜你喜欢

转载自blog.csdn.net/passer__jw767/article/details/111600405