HDU - 2553 N皇后问题 (初试回溯)

N皇后问题

Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u

Description

在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。

 

Input

共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
 

Output

共有若干行,每行一个正整数,表示对应输入行的皇后的不同放置数量。
 

Sample Input

    
    
1 8 5 0
 

Sample Output

  
  
1 92 10


二、解题思路:
    要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与前面的所有皇后不能在同一行、同一列、同一对角线,在这里我们可以以行优先,就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果最后一列也不能放置,则说明此时放置方法出错,则回到上一个皇后向之前放置的下一列重新放置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即可找出一个n皇后问题的所有可行解。

三、复杂度分析:
    关于N皇后问题的复杂度问题可以说是众说纷纭了,自己也改变过好几次,刚开始以为棋盘是n行n列,所以理所当然应该是n^2,后来发现在每列选择可否放置的比较上又做了一次循环,所以应该是n^3,但想了很久,发现判断可否放置的时候不是每次都循环到n,它是根据皇后i的取值而变化的,所以复杂度应该是1/3 n^3左右,即是小于n^3的。


以下两种代码是超时的代码,但是解题的思路是正确的,如有不理解的参见注释。

两种方法主要是用到了回溯法求解,第二种方法是用二维数组进行优化。

#include <stdio.h>
#include <string.h>
const int N = 15;
int C[N];
int tot;
int n;
void search(int cur) {
	if( cur == n) {
		tot++;
	}else {
		for(int i = 0; i < n; i++) {
			int ok = 1;
			C[cur] = i; //尝试把第cur行的皇后放到第i列
			for(int j = 0; j < cur; j++) { //检查是否和前面的皇后冲突
				if(C[cur] == C[j] || cur - C[cur] == j - C[j] || cur + C[cur] == j + C[j]) {
				//在同一行上       //在同一左斜对角线上         //在同一右斜对角线上
					ok = 0;
					break;
				}
			}
			if(ok) {
				search(cur+1);
			}
		}
	}
}
int main() {
	while( scanf("%d",&n) != EOF && n) {
		tot = 0;
		memset(C,0,sizeof(C));
		search(0);
		printf("%d\n",tot);
	}
	return 0;
}

但是经过优化后还是超时,看来只能打表了。

#include <stdio.h>
#include <string.h>
const int N = 15;
//int C[N];
int tot;
int n;
int vis[4][N*2];
void search(int cur) {
	if(cur == n) {
		tot++;
	}else {
		for(int i = 0; i < n; i++) {
			if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {
				//当前行被访问 //当前行右对角线被访问 //当前行左对角线被访问
				//C[cur] = i; //如果不用打印,数组C可以忽略
				vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;
				search(cur+1);
				vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;
			}
		}
	}
}
int main() {
	while( scanf("%d",&n) != EOF && n) {
		tot = 0;
		memset(vis,0,sizeof(vis));
		search(0);
		printf("%d\n",tot);
	}
	return 0;
}

AC代码

#include <stdio.h>
int ans[10]={1,0,0,2,10,4,40,92,352,724};
int main() {
	int n;
	while(scanf("%d",&n) != EOF && n) {
		printf("%d\n",ans[n-1]);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值