回溯法例题大全

最优装载问题

问题描述: 

有两个货轮,装载量分别为15吨与20吨,分别有10个集装箱,重量分别为4,7,3,3,4,5,3,4,1,1。现在要求用这两个货轮装下所有的集装箱,不计体积。

问题分析:

对于这个问题我们只需要考虑尽可能的去装满货轮1,剩余的集装箱全部放到C2中即可,也就是说我们相当于求解的是C1的最优装载,即C1最多能装多少。这个问题便转变成了一个特殊的0-1背包问题,特殊在重量与价值相等,显然我们可以使用动态规划算法求解,但是这里我们选择回溯法。

公式:

\sum_{i=0}^{t}wi*xi<=C1 \\\\ \sum_{i=0}^{t}wi*xi+\sum_{i=t+1}^{n-1}wi>bestC

代码:

/*
	问题描述:
	有两个货轮,装载量分别为15吨与20吨。
	分别有10个集装箱,重量分别为4,7,3,3,4,5,3,4,1,1。
	现在要求用这两个货轮装下所有的集装箱,不计体积。
*/
#include <iostream>
#include <vector>

using namespace std;

int C1, C2;
int n;
int bestC;
int countAll;
vector<int> containers;
vector<int> isChoosed;

//结果打印函数
void print() {
	cout << "C1的最优装载重量是:" << bestC<<" 吨"<<endl;
	cout << "C1选择的物品重量分别为:" << endl;
	for (int i = 0; i < n; i++) {
		if (isChoosed[i] == 1) {
			cout << "第 " << i << " 个集装箱重量 " << containers[i] << " 吨" << endl;
		}
	}
}	

//装载重量计算函数
int weightCount(int t) {
	int weight = 0;
	for (int i = 0; i <= t; i++) {
		if (isChoosed[i] == 1) {
			weight += containers[i];
		}
	}
	return weight;
}

//计算剩余重量的计算函数
int weightLeft(int t) {
	int weight = 0;
	for (int i = t + 1; i < n; i++) {
		weight += containers[i];
	}
	return weight;
}

//判断最后需要装载到货轮2的集装箱是否重量在规定范围内
bool weightC2() {
	int weightC1 = weightCount(n - 1);
	int weightC2 = countAll - weightC1;
	if (weightC2 > C2) {
		return false;
	}
}

//约束函数
bool bound(int t) {
	//计算出当前货轮1中已经装载的货物的重量
	int weight = weightCount(t);
	//如果求得的重量已经超过了货轮1的承重,那么返回false
	if (weight > C1) {
		return false;
	}
	return true;
}

//剪枝函数
bool cut(int t) {
	//如果发现当前方案下,已经装入的集装箱与剩余集装箱重量之和小于bestC
	//那么就直接放弃该方案,实行剪枝,返回false
	int weight = weightCount(t) + weightLeft(t);
	if (weight < bestC || weight == bestC && t == n - 1) {
		return false;
	}
	return true;
}

//回溯遍历函数
void backTrack(int t) {
	if (t >= n) {
		//记录当前最优装载方案
		if (weightC2()) {
			bestC = weightCount(n - 1);
			print();
		}
		return;
	}
	else {
		for (int i = 0; i <= 1; i++) {
			isChoosed[t] = i;
			if (bound(t) && cut(t)) backTrack(t + 1);
		}
	}	
}

int main() {
	bestC = 0;
	countAll = 0;
	//输入C1,C2的值
	cout << "输入C1,C2的值" << endl;
	cin >> C1;
	cin >> C2;
	//输入集装箱的总个数
	cout << "输入集装箱的总个数" << endl;
	cin >> n;
	//输入各个集装箱的重量
	for (int i = 0; i < n; i++) {
		int container;
		cout << "输入集装箱的重量" << endl;
		cin >> container;
		countAll += container;
		containers.push_back(container);
		isChoosed.push_back(0);
	}
	//输入完成之后开始回溯
	backTrack(0);
}

运行结果如下: 

C1的最优装载重量是:15 吨
C1选择的物品重量分别为:
第 4 个集装箱重量 4 吨
第 5 个集装箱重量 5 吨
第 7 个集装箱重量 4 吨
第 8 个集装箱重量 1 吨
第 9 个集装箱重量 1 吨

批处理作业调度问题

问题描述:

有n个待处理的作业,每个作业都需要在处理机A上处理完后再被处理机B处理,在处理机AB上的处理时间分别为a[i],b[i],在A、B上的结束时间分别为A[i]、B[i],现在要求\sum B[i]最小,求最优处理方案耗时。

问题分析:

要解决这个问题,我们首先要列出公式,首先是A[i]和A[j-1]的关系,然后是B[j]和A[j]还有B[j-1]的关系。

公式:

得到的公式如下: 

A[j]=A[j-1]+a[j]\\\\B[j]=max(A[j],B[j-1])+b[j]

 代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int n;//批处理作业的个数
int bestTime = 0;//最短时间
vector<int> x;//处理顺序数组
vector<int> a;//作业在A上处理所需时长数组
vector<int> A;//作业在A上被处理完时的时间数组
vector<int> b;//作业在B上处理所需时长数组
vector<int> B;//作业在B上被处理完成的时间数组

int countTime(int t) {
	int count = 0;
	for (int i = 0; i <= t; i++) {
		count += B[i];
	}
	return count;
}

void print() {
	cout << "最短的时间是:" << bestTime << endl;
}

bool bound(int t) {
	return true;
}

bool cut(int t) {
	if (bestTime == 0) {
		return true;
	}
	int nowTime = countTime(t);
	if (bestTime < nowTime || bestTime == nowTime && t < n - 1) {
		return false;
	}
	return true;
}

void backTrack(int t) {
	if (t >= n) {
		bestTime = countTime(n - 1);
		print();
		return;
	}
	else {
		for (int i = t; i < n; i++) {
			swap(x[t], x[i]);
			if (t) {
				A[t] = A[t - 1] + a[x[t]];
				B[t] = max(A[t], B[t - 1]) + b[x[t]];
			}
			else {
				A[t] = a[x[t]];
				B[t] = A[t] + b[x[t]];
			}
			if(bound(t)&&cut(t)) backTrack(t + 1);
			swap(x[t], x[i]);
		}
	}
}
	
int main() {
	cout << "请输入n的值:" << endl;
	cin >> n;
	/*x = vector<int>(n);
	a = vector<int>(n);
	b = vector<int>(n);
	A = vector<int>(n);
	B = vector<int>(n);*/
	for (int i = 0; i < n; i++) {
		int c;
		cout << "请输入作业 " << i << " 在A处理机上的运行时长";
		cin >> c;
		a.push_back(c);
		cout << "请输入作业 " << i << " 在B处理机上的运行时长";
		cin >> c;
		b.push_back(c);
		x.push_back(i);
		A.push_back(0);
		B.push_back(0);
	}

	backTrack(0);
}

运行结果如下:

请输入n的值:
3
请输入作业 0 在A处理机上的运行时长2
请输入作业 0 在B处理机上的运行时长1
请输入作业 1 在A处理机上的运行时长3
请输入作业 1 在B处理机上的运行时长1
请输入作业 2 在A处理机上的运行时长2
请输入作业 2 在B处理机上的运行时长3
最短的时间是:19
最短的时间是:18

n皇后问题:

问题描述: 

 n×n的棋盘,n个皇后,求n个皇后的放置方案个数。要求任意两个皇后都不在同一行、同一列或同一斜线上。

问题分析

过于简单。。。不赘述,直接上代码。

代码: 

#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

int n;//这个问题是n皇后问题
int resultCount = 0;
vector<int> x;

bool cut(int t) {
	for (int i = 0; i < t; i++) {
		if (abs(x[i] - x[t]) == abs(i - t)) {
			return false;
		}
	}
	return true;
}

void backTrack(int t) {
	if (t >= n) {
		resultCount++;
		return;
	}
	else {
		for (int i = t; i < n; i++) {
			swap(x[t], x[i]);
			if(cut(t)) backTrack(t + 1);
			swap(x[t], x[i]);
		}
	}
}

int main() {
	cout << "请输入n的值:" << endl;
	cin >> n;
	//初始化数组x
	for (int i = 0; i < n; i++) {
		x.push_back(i);
	}
	backTrack(0);
	cout << n << " 皇后问题解的个数为:" << resultCount << endl;
}

运行结果如下:

请输入n的值:
8
8 皇后问题解的个数为:92

m着色图问题

问题描述 :

给定无向连通图G和m种不同的颜色。用这些颜色为图G 的各顶点着色,每个顶点着一种颜色。是否有一种着色法,使G中每条边的2个顶点着不同颜色。

问题分析:

本题中,我们要求的是图的m着色问题,即给出的无向连通图是多少着色图,我们采用n * n维向量vector来表示邻接矩阵,遍历邻接矩阵之后我们可以判断各个节点之间是否相邻,相邻节点的着色不能相同,否则结束当前策略,回溯到上一步。

对于m的值,我们是不确定的,所以我们从1开始累加测试m的值。

 代码:

#include <iostream>
#include <cstdlib>
#include <vector>

using namespace std;

int m = 1;//假定为m着色图
int n;//点的个数
bool success = false;
vector<int> ei;
vector<vector<int>> e;//邻接矩阵
vector<int> point;

void print() {
	cout << "着色成功!" << endl;
	cout << "该图是:" << m - 1 << "色图";
}

bool cut(int t) {
	//遍历矩阵
	for (int i = 0; i <= t; i++) {
		for (int j = 0; j <= t; j++) {
			if (e[i][j] == 1 && point[i] == point[j] && i != j) {
				return false;
			}
		}
	}
	return true;
}

void backTrack(int t) {
	if (success) {
		return;
	}
	if (t >= n) {
		//输出结果
		print();
		//着色成功
		success = true;
		return;
	}
	else {
		for (int i = t; i < m; i++) {
			point[t] = i;
			if(cut(t)) backTrack(t + 1);
		}
	}
}

int main() {
	//输入节点个数
	cout << "请输入节点的个数" << endl;
	cin >> n;

	//初始化邻接矩阵
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			ei.push_back(0);
		}
		e.push_back(ei);
		ei.clear();
	}

	//初始化节点数组
	for (int i = 0; i < n; i++) {
		point.push_back(0);
	}

	int countE;
	cout << "请输入边的条数:" << endl;
	cin >> countE;

	for (int i = 0; i < countE; i++) {
		cout << "请输入第:" << i << "条边的两个顶点:" << endl;
		int pointA;
		int pointB;
		cin >> pointA;
		cin >> pointB;
		//在邻接矩阵中将对应边标记为1
		e[pointA][pointB] = 1;
		e[pointB][pointA] = 1;
	}
	while (!success) {
		backTrack(0);
		m++;
	}
}

输入:

请输入节点的个数
5
请输入边的条数:
8
请输入第:0条边的两个顶点:
0 1 0 2 0 3 1 2 1 3 1 4 2 3 3 4

输出:

着色成功!
该图是:4色图

至此,常见的例题已经解决完毕,感谢观看~~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值