C++回溯算法Demo:以4皇后问题为例

回溯算法实际上是构造一棵推理树,并由树的叶子节点反向输出历史步骤;

其中,树的构建过程较为复杂;一种简化的方法是使用链表连接和构造各个节点的关系;

以4皇后问题为例,采用C++ vector容器——避免使用指针(当然换成了整数来代替指针表达对象的位置),解决了该问题。整体算法思路清晰,便于理解。

见代码;与书中不同,此代码实际输出的是所有4皇后问题的不同走法

//title:4皇后问题的回溯算法求解
//Demo: 1)回溯算法实现4皇后问题;2)难点:树形结构的表达;3)用线性容器表达树形结构,并实现树的扫描——降低了树实现的难度
//author: Liping Chen
//email: [email protected]
//published date: 20125-4-11
#include <iostream>
#include <string.h>
#include <vector>
#include <stdlib.h>

using namespace std;

//定义4皇后棋局的数据结构及方法
typedef struct Queen4 {
	int vals[16];
	int nQueens;
	int parent;

	//默认构造函数
	Queen4() {
		for(int i = 0; i < 16; i++)
			vals[i] = 0;
		parent = 0;
		nQueens = 0;
	}

	//构造函数1
	Queen4(int nvals[16]) {
		for(int i = 0; i < 16; i++)
			vals[i] = nvals[i];
		parent = 0;
		nQueens = 0;
	}

	//找到当前布局中不为0的位置
	int getPosition() {
		for(int i = 0; i < 16; i++)
			if (vals[i] == 0) {
				return i;
			}
		return -1;
	}

	//当设置皇后位置时,标记水平、垂直和斜线位置掩码
	void setQueen(int pos) {
		int row, col;
		vals[pos] = 1;
		nQueens++;
		row = pos / 4;
		col = pos % 4;
		for(int c = 1; c <= 3; c++) {
			//右下
			if (row + c < 4 && col + c < 4)
				if (vals[(row + c) * 4 + (col + c)] == 0)
					vals[(row + c) * 4 + (col + c)] = 2;
			//左上
			if (row - c >= 0 && col - c >= 0)
				if (vals[(row - c) * 4 + (col - c)] == 0)
					vals[(row - c) * 4 + (col - c)] = 2;
			//左下
			if (row + c < 4 && col - c >= 0)
				if (vals[(row + c) * 4 + (col - c)] == 0)
					vals[(row + c) * 4 + (col - c)] = 2;
			//右上
			if (row - c >= 0 && col + c >= 0)
				if (vals[(row - c) * 4 + (col + c)] == 0)
					vals[(row - c) * 4 + (col + c)] = 2;
			//右水平
			if (col + c < 4)
				if (vals[row * 4 + (col + c)] == 0)
					vals[row * 4 + (col + c)] = 2;
			//左水平
			if (col - c >= 0)
				if (vals[row * 4 + (col - c)] == 0)
					vals[row * 4 + (col - c)] = 2;
			//下
			if (row + c < 4)
				if (vals[(row + c) * 4 + col] == 0)
					vals[(row + c) * 4 + col] = 2;
			//上
			if (row - c >= 0)
				if (vals[(row - c) * 4 + col] == 0)
					vals[(row - c) * 4 + col] = 2;
		}
	}

	//输出当前棋局
	void output(int level) {
		int cnt = 0;
		char chars[100];
		for(int k = 0; k < level; k++)
			chars[k] = ' ';
		chars[level] = '\0';
		cout << chars << "Queen4=" << endl  << chars;
		for(int i = 0; i < 16; i++) {
			cout << vals[i] << " ";
			cnt++;
			if (cnt % 4 == 0) cout << endl << chars;
		}
	}

	//递归调用输出历史棋局
	void outputHist(vector<Queen4>& tr) {
		if (parent)
			tr[parent].outputHist(tr);
		output(0);
	}

	//由棋的当前布局产生下一布局
	void reproduce(vector<Queen4>& tr, int pos) {
		int nvals[16];
		bool inserted;
		//思考:为什么要使用nvals
		for(int i = 0; i < 16; i++)
			nvals[i] = vals[i];
		for(int i = 0; i < 16; i++) {
			if (nvals[i] == 0) {
				nvals[i] = 1;
				//新结果加入容器
				Queen4 q(tr[pos].vals);
				q.setQueen(i);
				q.parent = pos;
				tr.push_back(q);
			}
		}
	}
}Queen4;

//程序主函数
int main() {
	Queen4 q0;								//调用默认构造函数
	vector<Queen4> tr;						//向量容器——作用相当于队列,可以向期中添加新的棋盘布局
	int levels[1024] = {0};					//记录每层的孩子数量——用于分层

	tr.push_back(q0);						//将初始棋盘加入容器
	int oldn = 0, newn = 1, level = 0;      //存储变量
	//让根节点产生新孩子,并把新孩子加入容器
	//若不再产生新孩子了,则认为已找到答案
	//那么,最底层的就是答案(需要记录每层所产生的孩子数)
	while(newn != oldn) {
		//让最后的孩子再产生新孩子
		for(int i = oldn; i < newn; i++) tr[i].reproduce(tr, i);
		//更新老孩子和新孩子的数量
		oldn = newn;
		levels[++level] = newn;
		newn = tr.size();
	}
	oldn = 1;
	//输出4皇后问题共有多少种解法
	for(int i = levels[level-1]; i < levels[level]; i++) {
		cout << "4皇后放置走法:" << oldn++ << endl;
		tr[i].outputHist(tr);
	}
	return 0;
}
时间: 2024-11-05 22:55:00

C++回溯算法Demo:以4皇后问题为例的相关文章

回溯算法--八皇后问题

问题描述:八皇后不能相互攻击,即,八个皇后不能同行,同列,不同在同一条对角线上, 对角线又可以分为左对角线和右对角线 左对角线上满足:i-j+7都相等(i,j分别是一维和二维的坐标) 右对角线满足:i+j 都相等 代码如下: #include <iostream> using namespace std; int e_q[8][8]; int r[15]; int le[15]; int q[8]; int count = 0; void en_queens(int); int main()

回溯算法解八皇后问题(java版)

八皇后问题是学习回溯算法时不得不提的一个问题,用回溯算法解决该问题逻辑比较简单. 下面用java版的回溯算法来解决八皇后问题. 八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 思路是按行来规定皇后,第一行放第一个皇后,第二行放第二个,然后通过遍历所有列,来判断下一个皇后能否放在该列.直到所有皇后都放完,或者放哪

8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化

上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来探讨一下非递归方案 实验结果令人还是有些失望,原来非递归方案的性能并不比递归方案性能高 代码如下: package com.newflypig.eightqueen; import java.util.Date; /** * 使用循环控制来实现回溯,解决N皇后 * @author [email pr

【八皇后问题】 回溯算法

回溯算法:回溯算法实际上是一个类似枚举的搜索尝试方法,它的思想是在搜索尝试中寻找问题的解,当发现不满足求解条件时,就“回溯”返回,尝试别的路径.之前介绍的基础算法中的贪婪算法,动态规划等都具有“无后效性”,也就是在分段处理问题时,某状态一旦确定,将不再改变.而多数问题很难找到"无后效性”的阶段划分和相应决策,而是通过深入搜索尝试和回溯操作完成的. 八皇后问题:8*8的国际象棋棋盘中放八个皇后,是任意两个皇后不能互相吃掉.规则:皇后能吃掉同一行,同一列,同一对角线的任意棋子. 模型建立:不妨设八个

经典回溯算法(八皇后问题)详解

八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题.该问题是十九世纪著名的数学家高斯1850年提出: 在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上 (斜率为1),问有多少种摆法.高斯认为有76种方案. 1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果. 计算机发明后,有多种方法可以解决此问题. 算法思路:    首先我们分析一下问题的解,我们每取出一个皇后,放入一行,共有八种不同的放法

回溯算法-C#语言解决八皇后问题的写法与优化

结合问题说方案,首先先说问题: 八皇后问题:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 嗯,这个问题已经被使用各种语言解答一万遍了,大多还是回溯法解决的. 关于回溯算法:个人理解为就是优化的穷举算法,穷举算法是指列出所有的可能情况,而回溯算法则是试探发现问题"剪枝"回退到上个节点,换一条路,能够大大提高求解效率. 具体到8皇后问题上来说,需要考虑以下几点: 1)将8个皇后定义为8行中的相对位置来标识,考虑增

C语言回溯算法解决N皇后问题

回溯算法的模型是 x++, not satisfy ? x-- : continue. 代码中x作列号,y[x]保存第x列上皇后放置的位置. 1 #include<stdio.h> 2 #include<math.h> 3 #define N 5 4 int position_check(int,int*); 5 void print_board(int count,int* y); 6 int main() 7 { 8 int y[N]= {0}; //记录每列上的皇后放的位置

回溯算法之八皇后问题

1 package 回溯; 2 3 public class 八皇后递归回溯 { 4 5 private int length; //皇后数量 6 private int sum; //总方案数 7 private int num[]; //存放皇后的一维数组 8 9 public 八皇后递归回溯() { 10 length = 8; 11 sum=0; 12 num = new int[length + 1]; 13 } 14 15 public boolean check(int x) {

8皇后问题SQL求解(回溯算法)

问题 八皇后问题是一个古老而著名的问题,是回溯算法的典型例题.该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法 百度来的代码 回溯法用递归实现八皇后解法 declare type t_queen is varray(8) of number; queen t_queen := t_queen(1, 2, 3, 4, 5, 6, 7, 8); l_num number := 0;