全排列详解及求N皇后全解


2019.01.23 日间温暖,夜间生寒,萎靡的一天~


美食菜单如下

 1,全排列介绍及实现(哈希映射应用)
 
 2,基于全排列求N皇后全解
 
 3,求解N皇后全解的优化及优化前后的时间对比

全排列介绍及实现

全排列大家应该都很熟悉,可如何高效编程实现或许有些陌生,以下将详细说明

设计思路

  • 1,适用范围
    输入: 1~n的自然数
    输出:n个数的全排列所有情况
  • 2,数据结构
    数组P存储当前全排列
    将需排列的数列作为数组下标(哈希映射),相应数组值表示状态(是否在全排列中)
const int maxn = 11;//假设最多只有11个数
//三个依次表示:n个数;P存储当前全排列;哈希表记录下标是否在全排列中
int n,P[maxn],hashTable[maxn] = {false};
  • 3,递归设计
    边界:index=n+1时表示一个全排列选取成功,输出后返回即可
    递归式:当未到达边界时,扫描n个数是否已被选中,若已选中,扫描下一个;
    若未选中,将x填入P中,同时将散列表赋真,继续填充下一个位置,结束时将相应散列表赋假

完整Code

#include<cstdio>

const int maxn = 11;
int n,P[maxn],hashTable[maxn] = {false};
//函数功能:给第index位填充数字 
void generateP(int index)
{
	//给第n+1填充,说明填充结束 
	if(index == n+1){
		for(int i = 1; i <= n; i++){//打印输出 
			printf("%d",P[i]);
		}
		printf("\n");
		return;
	}
	//选取一个未被选中的数填充第index位 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){//未被选中 
			P[index] = x;//填入 
			hashTable[x] = true;//哈希表状态改变 
			generateP(index+1);//填充下一个位置 
			hashTable[x] = false;//结束将位置让出来 
		}
	}
}
int main()
{
	n = 4;//改变此数字即可实现个数为1~11的全排列 
	generateP(1);
	return 0;
} 

测试

测试数据:n=3

测试结果
在这里插入图片描述

N皇后全解

该解法仅需稍微修改上述全排列算法即可,具体如下

求解N皇后最直接的思路是将N*N个点进行全排列,然后判断是否满足条件,但时间复杂度太高了。或者是利用栈来实现,类似深度搜索,可参考八皇后栈递归实现

在这里假设当前摆放满足条件,将每一列的点对应的行数抽出必定为一个全排列(或者行列互换,由于对称性,不影响结果),所以,我们仅需求出行(N个点)的全排列,然后判断即可

这里由于是先求解出行的全排列,所以行列必不相同,仅需判断两点是否位于同一对角线(正/斜对角线)即可

判断函数

  • 功能:判断两点是否在同一主/斜对角线上
  • 返回:是–>true;否–>false
bool isOk(int ix,int iy,int jx,int jy)
{
	//主、斜对角线 
	if(ix-jx == iy-jy || ix-jx == -iy+jy)return false;
	else return true;
}

核心主体
在全排列基础上增加判断
任意一种摆放成立的情况下,其每一行对应的列数必定是一种全排列。所以我们只要将全排列求出来,再对其进行判断即可,由于筛选出来的行列必不相同,所以仅需判断是否位于同一对角线上即可

void generateP(int index)
{
	if(index == n+1){
		bool bflag = true;//标记是否满足条件
		//对任意两个点判断 
		for(int i = 1; i <= n; i++){
			for(int j = i+1; j <= n; j++){
				if(!isOk(i,P[i],j,P[j])){//只要有一个不满足,立即跳出 
					bflag = false;
					break;
				}
			}
		}
		//满足条件输出并统计总个数 
		if(bflag){
			sum++;//统计解个数
			for(int i = 1; i <= n; i++){
				printf("%d",P[i]);
			}
			printf("\n");
		}
		return;
	}
	//全排列核心 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){
			P[index] = x;
			hashTable[x] = true;
			generateP(index+1);
			hashTable[x] = false;
		}
	}
}

算法优化

优化则是改变判断位置,每一次填充位置时对已有序列(1~index-1)进行判断即可,若不符合,立即跳转下一个数;若符合,继续判断

//优化代码 
void generateP_Refined(int index)
{
	if(index == n+1){
		sum++;
		for(int i = 1; i <= n; i++){
			printf("%d",P[i]);
		}
		printf("\n");
		return;
	}
	//全排列核心 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){
			//优化判断,将x与1~index-1个数判断是否满足条件 
			bool bflag = true;//标记是否满足条件
			for(int i = 1; i < index; i++){
				if(!isOk(i,P[i],index,x)){//只要有一个不满足,立即跳出 
					bflag = false;
					break;//若有一个不满足,立刻退出 
				}
			}
			if(!bflag)continue;//尝试下一个数 
			
			P[index] = x;
			hashTable[x] = true;
			generateP_Refined(index+1);
			hashTable[x] = false;
		}
	}
}

时间对比

注释掉输出语句后的执行时间
在这里插入图片描述

完整Code

#include<cstdio>
#include<ctime>

const int maxn = 11;
int n,P[maxn],hashTable[maxn]={false};
int sum = 0;
//判断两点是否在同一主/斜对角线上
//是-->true;否-->false 
bool isOk(int ix,int iy,int jx,int jy)
{
	//主、斜对角线 
	if(ix-jx == iy-jy || ix-jx == -iy+jy)return false;
	else return true;
}
//假设n皇后的一种摆放成立,其每一行对应的列数必定是一种全排列。所以我们只要将全排列求出来,再对其进行判断即可,
//由于筛选出来的行列必不相同,所以仅需判断是否位于同一对角线上即可 
void generateP(int index)
{
	if(index == n+1){
		bool bflag = true;//标记是否满足条件
		//对任意两个点判断 
		for(int i = 1; i <= n; i++){
			for(int j = i+1; j <= n; j++){
				if(!isOk(i,P[i],j,P[j])){//只要有一个不满足,立即跳出 
					bflag = false;
					break;
				}
			}
		}
		//满足条件输出并统计总个数 
		if(bflag){
			sum++;
			for(int i = 1; i <= n; i++){
				printf("%d",P[i]);
			}
			printf("\n");
		}
		return;
	}
	//全排列核心 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){
			P[index] = x;
			hashTable[x] = true;
			generateP(index+1);
			hashTable[x] = false;
		}
	}
}
//优化代码 
void generateP_Refined(int index)
{
	if(index == n+1){
		sum++;
		for(int i = 1; i <= n; i++){
			printf("%d",P[i]);
		}
		printf("\n");
		return;
	}
	//全排列核心 
	for(int x = 1; x <= n; x++){
		if(!hashTable[x]){
			//优化判断,将x与1~index-1个数判断是否满足条件 
			bool bflag = true;//标记是否满足条件
			for(int i = 1; i < index; i++){
				if(!isOk(i,P[i],index,x)){//只要有一个不满足,立即跳出 
					bflag = false;
					break;//若有一个不满足,立刻退出 
				}
			}
			if(!bflag)continue;//尝试下一个数 
			
			P[index] = x;
			hashTable[x] = true;
			generateP_Refined(index+1);
			hashTable[x] = false;
		}
	}
}

int main()
{
	int startT,endT;
	n = 8;

	startT = clock();
	generateP(1);
	endT = clock();
	printf("\n%d",sum);
	printf("\n优化前时间:%d\n",endT-startT);
	
	sum = 0; 
	startT = clock();
	generateP_Refined(1);
	endT = clock();
	printf("\n%d",sum);
	printf("\n优化后时间:%d",endT-startT);

	return 0;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值