算法分析与设计实验报告四——回溯法实验

一、实验目的

  1. 掌握回溯算法思想
  2. 掌握回溯递归原理
  3. 了解回溯法典型问题

二、实验内容

  1. 编写一个简单的程序,解决8皇后问题。
  2. 批处理作业调度问题
    [问题描述]给定n个作业的集合J=(J1, J2, … , Jn)。每一个作业Ji都有两项任务需要分别在2台机器上完成。每一个作业必须先由机器1处理,然后再由机器2处理。作业Ji需要机器i的处理时间为tji,i=1,2, … ,n; j=1,2。
    对于一个确定的作业调度,设Fji是作业i在机器i上完成处理的时间。则所有作业在机器2上完成处理的时间和成为该作业调度的完成时间和。
    在这里插入图片描述
    批处理作业调度问题要求对于给定的n个作业,制定一个最佳的作业调度方案,使其完成时间和达到最小。
    要求输入:
    1)作业数 2)每个作业完成时间表:
    在这里插入图片描述
    要求输出: 1)最佳完成时间 2)最佳调度方案
    提示:算法复杂度为O(n!),建议在测试的时候n值不要太大,可以考虑不要超过12。
  3. 数字全排列问题
    任意给出从1到N的N个连续的自然数,求出这N个自然数的各种全排列。如N=3时,共有以下6种排列方式:123,132,213,231,312,321。
    注意:数字不能重复,N由键盘输入(N<=9)。

三、算法思想分析

  1. 8皇后问题
    基本思想为一个基本的回溯,从最开始的空棋盘先放一个棋子,然后根据放置的棋子推演出一个新的局面,然后下下一行的棋子,一直递归的下下去直到最后一行下好,这样一个情况就产生成功;如果在中途的某一行没有地方可以下子,则恢复上一局面,在上一行重新下子,这样通过树形的穷举,即回溯的状态空间树,就可以将所有可能都推演一遍。
    实现时的算法描述为:
    在这里插入图片描述
    安全算法描述:
    判断竖直线,主对角线,副对角线是否有棋子
  2. 批处理作业调度问题
    基本思想为一个排序树,根据排序树每次推演的情况得到该情况所需调度时间,并与当前最优方案比较,如果调度时间更小,则将当前最小时间与当前情况储存,直到所有情况都已经推演完毕,最后输出结果。
    排列树思想基于一个交换的过程,每次推演时将当前结点与未使用的结点进行交换,得到多种情况,每种情况同样递归调用算法进行推演,最后每个结点都推演过自己的子集,这样所有的情况就能推演全。
    算法描述
    在这里插入图片描述
  3. 数字全排列问题
    基本思想完全就是一个排列树,每次完成一次推演输出当前情况即可。
    排列树算法描述请见第二题算法思想概述,在这里不再赘述。

四、实验过程分析
本次实验重点练习了回溯的相关内容,而回溯算法由于其有线索的性质,可以通过一个线索递归调用函数将所有情况推演出,而非单纯的去穷举,这样在穷举过程中就可以有条理地交与机器去执行。在实验中由于理解不够,在做皇后问题时忘记了对现场的恢复,一度导致整个局面十分混乱,归根结底是对回溯的思想理解的不透,在将8皇后完整地实现之后,在做排列树时就相对有针对性了,虽然推演的方法不同,但根据判定—改线索向下推进—递归调用自身—恢复现场的方法,可以对排列树的思想较快的理解,这是我十分可喜的收获。
同时通过本次实验,我深切感受到了回溯法的算法复杂度确实较大,如8皇后问题需要秒为单位的时间才能解决,这和之前的时间比是十分长的。

五、算法源代码及用户屏幕
1.8皇后问题
①代码

#include <iostream>
using namespace std;
#define NUM 50
int queen(int res[], int i, int chess[][NUM], int column[], int left[], int right[], int n) 	{
	for (int j = 0; j < n; j++) {
		//判断是否安全
		if (!column[j] && !left[i + j] && !right[i - j + n]) {
			//放皇后
			chess[i][j] = column[j] = left[i + j] = right[i - j + n] = 1;
			//判断是否到最后一行
			if (i == n - 1) {
				res[0]++;
				cout << "situation " << res[0] << endl;
				for (int i = 0; i < n; i++) {
					for (int j = 0; j < n; j++) {
						if (j == n - 1)cout << chess[i][j] << endl;
						else cout << chess[i][j] << " ";
					}
				}
				cout << endl;
			}
			//如果没有,下挖
			else queen(res, i + 1, chess, column, left, right, n);
			//去皇后
			chess[i][j] = column[j] = left[i + j] = right[i - j + n] = 0;
		}
	}
	return res[0];
}
int main() {
	int n;
	cout << "Please input the size of chessboard: ";
	cin >> n;
	int chess[NUM][NUM] = { 0 };
	int column[n] = { 0 };
	int left[2 * n] = { 0 };
	int right[2 * n] = { 0 };
	int count = 0;
	int res[1] = { 0 };
	res[0] = queen(res, 0, chess, column, left, right, n);
	cout << "the total number of the situation is: " << res[0];
	system("pause");
	return 0;
}

②用户界面
首先输入棋盘格数,然后回车返回各个情况的内容与所有情况的个数。
在这里插入图片描述
在这里插入图片描述
2. 批处理作业调度问题
①代码

#include <iostream>
using namespace std;
#define NUM 100
void jobScheduling(int jobs[][NUM], int m, int i, int min[], int temp[]) {

	if (i > m) {
		for (int i = 1; i <= m; i++) {
			cout << temp[i] << " ";
		}
		int sum = 0;
		for (int j = 1; j <= m; j++) {
			for (int k = 1; k <= j; k++) {
				sum += jobs[k][1];
			}
			sum += jobs[j][2];
		}
		cout << sum << endl;
		if (sum < min[0]) {
			min[0] = sum;
			for (int j = 1; j <= m; j++) {
				min[j] = temp[j];
			}
		}
	}
	else {
		for (int t = i; t <= m; t++) {
			//交换
			for (int k = 1; k <= 2; k++) {
				int temp = jobs[t][k];
				jobs[t][k] = jobs[i][k];
				jobs[i][k] = temp;
			}
			int tmp = temp[t];
			temp[t] = temp[i];
			temp[i] = tmp;
			//递归向下调度
			jobScheduling(jobs, m, i + 1, min, temp);
			//恢复现场
			for (int k = 1; k <= 2; k++) {
				int temp = jobs[t][k];
				jobs[t][k] = jobs[i][k];
				jobs[i][k] = temp;
			}
			tmp = temp[t];
			temp[t] = temp[i];
			temp[i] = tmp;
		}
	}
}
int main() {
	int m;
	cout << "Please input the number of jobs: ";
	cin >> m;
	cout << "Please input the time of each job and each machine: ";
	int jobs[NUM][NUM];
	int min[m + 1];
	int temp[m + 1];
	min[0] = 1000;
	for (int i = 1; i < m + 1; i++) {
		min[i] = i;
		temp[i] = i;
	}
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= 2; j++) {
			cin >> jobs[i][j];
		}
	}
	jobScheduling(jobs, m, 1, min, temp);
	cout << "the minimum of the total scheduling time is: " << min[0] << endl;
	cout << "the scheduling mechod is: ";
	for (int i = 1; i <= m; i++) {
		cout << min[i] << " ";
	}
	system("pause");
	return 0;
}

②用户界面
首先输入作业个数,然后输入每个作业在机器1与机器2上运行的时间,然后回车输出每个情况的结果与所求的最小时间结果与对应情况。
在这里插入图片描述
3. 数字全排列问题
①代码

#include<iostream>
using namespace std;
#define NUM 20
void allSort(int i, int n, int num[]) {
	if (i >= n) {
		for (int i = 0; i < n; i++) {
			cout << num[i] << " ";
		}
		cout << endl;
	}
	else {
		for (int t = i; t < n; t++) {
			int temp = num[t];
			num[t] = num[i];
			num[i] = temp;

			allSort(i + 1, n, num);

			temp = num[t];
			num[t] = num[i];
			num[i] = temp;
		}
	}
}
int main() {
	int num;
	cout << "Please input the number: ";
	cin >> num;
	int n[num];
	for (int i = 0; i < num; i++) {
		n[i] = i + 1;
	}
	allSort(0, num, n);
	system("pause");
	return 0;
}

②用户界面
首先输入数字个数,然后回车显示所有情况。
在这里插入图片描述

  • 5
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值