MOOC清华《程序设计基础》第5章:N皇后问题(带矩阵输出)

哈哈!经过三天的纠结debug之后,激动人心的、展示计算机作为伟大的智能机器的“N皇后问题”计算程序新鲜出炉啦!还带矩阵输出哦!是不是很像棋盘?

//问题描述:
//在N×N的棋盘上,放置N个皇后,使两两之间互不攻击。所谓互不攻击是指:
//(1)不在棋盘的同一行;
//(2)不在棋盘的同一列;
//(3)不在棋盘的同一对角线上。
//第一次输入:N
//第一次输出:共有多少组解
//第二次输入:输出第几组解
//第二次输出:以矩阵形式输出这一组解 

#include <iostream> 
#include <iomanip>
using namespace std;

int N;
int Normalize;  //用来统一数组下标

int Num = 0;  //方案数

int q[101];  //N个皇后所占用的行号,可是定义数组时下标不能是变量
			//所以就暂估计一个较大的值N = 100吧 

bool C[101];  //S[1]~S[N],当前列是否安全
bool L[201];  //L[2]~L[2 * N],(i - j)对角线是否安全
bool R[201];  //R[2]~R[2 * N],(i + j)对角线是否安全

int main()
{
	for(int i = 0; i < 101; i++)
		C[i] = true;
	for(int i = 0; i < 201; i++)
	{
		L[i] = true;
		R[i] = true;
	}
	
	cout << "请输入N:";
	cin >> N;
	Normalize = N + 1;
	
	void Try(int row);
	void Output(int row, int n);
	
	Try(1);  //从第1行开始放皇后
	
	cout << "总共有" << Num << "组解!" << endl;
	
	int x = 0;
	cout << "请问需要输出第几组解?:";
	cin >> x;
	while(x > Num || x < 1) 
	{
		cout << "数据越界!请重新输入!:";
		cin >> x;
	}	
	
	Num = 0;  //Num重新置零,非常重要!
	 
	Output(1, x);	
	//cout << "Num = " << Num << endl;

	return 0; 
}

void Try(int row)
//除了对角线的处理外,N皇后问题的第二个技巧就是递归参数的选择,
//想一想,为什么没有选棋盘的规模N作为递归参数?而是选了行号或列号? 
{
	for(int col = 1; col <= N; col++)
	//依次尝试当前的N列位置
	{
		//判断拟放置皇后的位置是否安全
		if(C[col] && L[row - col + Normalize] && R[row + col])
		{
			//记录位置信息(行号)
			q[row] = col;
			
			//修改三个方向的安全性标记
			C[col] = false;
			L[row - col + Normalize] = false;
			R[row + col] = false;
			//核心技巧其实在后面这两行里,只有这样调整,对角线的下标才统一在一个相同的区间里
			
			if(row < N)
			{
				//递归尝试放下一行 
				Try(row + 1);
			}
			else
			{
				Num++;
			}
			
			//回溯:恢复三个方向原有安全性
			C[col] = true;
			L[row - col + Normalize] = true; 
			R[row + col] = true;
		} 
	} 
} 

void Output(int row, int n)
{
	for(int col = 1; col <= N; col++)
	//依次尝试当前的N列位置
	{
		//判断拟放置皇后的位置是否安全
		if(C[col] && L[row - col + Normalize] && R[row + col])
		{
			//记录位置信息(行号)
			q[row] = col;
			
			//修改三个方向的安全性标记
			C[col] = false;
			L[row - col + Normalize] = false;
			R[row + col] = false;
			//核心技巧其实在后面这两行里,只有这样调整,对角线的下标才统一在一个相同的区间里 
			
			if(row < N)
			{
				//递归尝试放下一行,注意这里的函数是Output(),而不是Try() 
				Output(row + 1, n);
			}
			else
			{
				Num++;
				if(Num == n)
				{
					cout << endl;
					cout << "第" << n << "组解为:" << endl;
					cout << endl;
					cout << setiosflags(ios::right) << setw(4) << " ";
					for(int i = 1; i <= N; i++)
						cout << setiosflags(ios::right) << setw(4) << i;
					cout << endl << endl;
					for(int i = 1; i <= N; i++)
					{
						cout << setiosflags(ios::right) << setw(4) << char(65 + i - 1);
						for(int j = 1; j <= N; j++)
						{
							if(j != q[i])
								cout << setiosflags(ios::right) << setw(4) << '0';
							else
								cout << setiosflags(ios::right) << setw(4) << '1';
						}
						cout << endl << endl;
					}
				}
			}
			
			//回溯:恢复三个方向原有安全性
			C[col] = true;
			L[row - col + Normalize] = true; 
			R[row + col] = true;
		} 
	} 
}

以N = 15为例,输出结果如下!:


稍作修改后,N=16时的结果:(N=16可能是可以输出的最大值了,N=16也要算2分多钟才能算完,我的电脑CPU酷睿i5。N=17基本上算不出来,我等了五六分钟都没结果,具体需要多久算出来,可能要结合《计算机组成原理》中的知识。)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值