N皇后问题

问题描述:在一个N * N的棋盘中放置N个皇后,使得每个皇后都不在同一行、同一列和同一斜线上,总共有多少种放置方法。

思路:遍历棋盘的每一行,在该行中遍历列来寻找合适的位置放置皇后。如果找到合适的位置,放置皇后之后,跳到下一行。否则,回溯到上一行,找到该行下一个合适的列来放置皇后,再往下遍历。

在编程的具体实现中,可以用二维数组或一维数组两种方式来表示棋盘的行和列,回溯法也有递归和非递归(即迭代)两种方法来实现。

通过测试程序运行时间的比较,我们可以得出结论:使用迭代的程序比使用递归的程序快,使用一维数组的程序·比使用二维数组的程序快。

代码如下:

//----------------------------------【n皇后问题】------------------------------------

#include <iostream>
#include <vector>
#include <time.h>
#define UNPUT -1
using namespace std;

//-------------------------【方法1:回溯法递归 + 二维数组】--------------------------
class NQueens1
{
public:
	NQueens1(int num = 8)
	{
		n = num;
		count = 0;
		for(int i = 0; i < n; ++i)
		{
			vector<bool> temp;
			for(int j = 0; j < n; ++j)
			{
				temp.push_back(false);
			}
			board.push_back(temp);
		}
	}
	// 利用回溯法递归
	void put(int i = 0)
	{
		if(i == n)
			return;
		else
		{
			for(int j = 0; j < n; ++j)
			{
				board[i][j] = true;
				if(isSafe(i, j))
				{
					put(i+1);
				}
				board[i][j] = false;
			}
		}
	}
	// 测试程序执行时间
	void test() 
	{
		clock_t start = clock();
		put(0);
		clock_t end = clock();
		double totol_time = end - start;
		printf("%f\n", totol_time);
	}
private:
	// 确定row行col列的皇后位置是否合法
	bool isSafe(int row, int col) const
	{
		for(int i = 0; i < row; ++i)
		{
			for(int j = 0; j < n; ++j)
			{
				if(board[i][j] == true && (j == col 
					|| abs(row-i) == abs(col-j)))
				{
					return false;
				}
			}
		}
		return true;
	}
	// 打印二维数组中皇后的位置
	void print() 
	{
		printf("case%d\n", ++count);
		for(int i = 0; i < n; ++i)
		{
			for(int j = 0; j < n; ++j)
			{
				board[i][j] ? printf("%c ", 2) : printf(". ");
			}
			cout << endl;
		}
	}
private:
	int n;                         // 皇后个数,也是行数和列数
	int count;                     // 解的个数
	vector<vector<bool>> board;   // 二维数组,表示某位置是否放置皇后
};



//-------------------------【方法2:回溯法递归 + 一维数组】--------------------------
class NQueens2
{
public:
	NQueens2(int num = 8)
	{
		n = num;
		count = 0;
		for(int i = 0; i < n; ++i)
		{
			for(int j = 0; j < n; ++j)
			{
				colIndex.push_back(UNPUT);
			}
		}
	}
	// 利用回溯法递归
	void put(int i = 0) 
	{
		if(i == n)
			return;
		else
		{
			for(int j = 0; j < n; ++j)
			{
				colIndex[i] = j;
				if(isSafe(i))
					put(i+1);
				colIndex[i] = UNPUT;
			}
		}
	}
	// 测试程序执行时间
	void test() 
	{
		clock_t start = clock();
		put(0);
		clock_t end = clock();
		double totol_time = end - start;
		printf("%f\n", totol_time);
	}
private:
	// 确定皇后位置是否合法
	bool isSafe(int row) const
	{
		for(int i = 0; i < row; ++i)
		{
			if(colIndex[i] >= 0 && (colIndex[i] == colIndex[row] 
				|| abs(row-i) == abs(colIndex[row]-colIndex[i])))
			{
				return false;
			}
		}
		return true;
	}
	// 打印所有皇后位置
	void print() 
	{
		printf("case%d\n", ++count);
		for(int i = 0; i < n; ++i)
		{
			for(int j = 0; j < n; ++j)
			{
				if(j == colIndex[i])
					printf("%c ", 2);
				else
					printf(". ");
			}
			cout << endl;
		}
	}
private:
	int n;                 // 皇后个数
	int count;             // 解的个数
	vector<int> colIndex;  // 一维数组,表示在i行colIndex[i]列处放置皇后,
	                       // colIndex[i] = -1 表示不放置皇后
};



//-------------------------【方法3:回溯法非递归 + 一维数组】--------------------------
class NQueens3
{
public:
	// 初始化
	NQueens3(int num = 8)
	{
		n = num;
		count = 0;
		for(int i = 0; i < n; ++i)
		{
			board.push_back(UNPUT);
		}
	}
	// 放置皇后
	void put()
	{
		int i = 0, j = 0;           // i代表当前行,j代表当前列
		while(i < n)
		{
			while(j < n)
			{
				if(isSafe(i, j))   
				{
					board[i] = j;   // 若当前位置合法,则放置皇后
					j = 0;          // 当前列重新设为0,即为棋盘第一列
					break;
				}
				else
				{
					++j;            // 若当前位置不合法,则寻找该行下一列位置
				}
			}

			if(board[i] == UNPUT)   // 若在该行没有合适位置
			{
				if(i == 0)          // 若为第一行,则无需回溯
					break;
				else
				{
  					i--;				// 若不是第一行,则回到上一行
  					j = board[i] + 1;	// 并找到该行下一列位置
  					board[i] = UNPUT;   // 将该行原来放置的皇后去掉
  					continue;
				}
			}
			if(i == n-1)                // 若是最后一行
			{
				//print();                // 打印该解
				j = board[i] + 1;		// 找到该行下一列位置
				board[i] = UNPUT;		// 将该行原来位置的皇后去掉
				continue;
			}
			++i;
		}
	}
	// 测试程序
	void test() 
	{
		clock_t start = clock();
		put();
		clock_t end = clock();
		double totol_time = end - start;
		printf("%f\n", totol_time);
	}
private:
	// 判断该位置是否合法
	bool isSafe(int row, int col) const
	{
		for(int i = 0; i < n; ++i)
		{
			if(board[i] >= 0 && (board[i] == col 
				|| abs(row-i) == abs(col-board[i])))
				return false;
		}
		return true;
	}
	// 打印当前解
	void print() 
	{
		printf("case%d\n", ++count);
		for(int i = 0; i < n; ++i)
		{
			for(int j = 0; j < n; ++j)
			{
				if(j == board[i])
					printf("%c ", 2);
				else
					printf(". ");
			}
			cout << endl;
		}
	}
private:
	int n;                // 皇后个数
	int count;			  // 解的个数
	vector<int> board;    // 一维数组代表棋盘
};



// 测试和比较三种方法
int main(int argc, char const *argv[])
{
	NQueens1 q1;
	NQueens2 q2;
	NQueens3 q3;
	q1.test();
	q2.test();
	q3.test();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值