HDU-OJ-2553 N皇后问题(打表/位运算)

37 篇文章 0 订阅
14 篇文章 0 订阅

N皇后问题

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


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

 

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

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

Sample Input
  
  
1 8 5 0
 

Sample Output
  
  
1 92 10
 

Author
cgf
 

————————————————————深夜的分割线————————————————————
思路:N皇后问题,dfs嘛!咱们看一个可以AC的答案:
#include <stdio.h>
int main() {
    int n;
	while(scanf("%d", &n) != EOF&&n) {
        switch(n){
            case 1:puts("1");break;
            case 2:puts("0");break;
            case 3:puts("0");break;
            case 4:puts("2");break;
            case 5:puts("10");break;
            case 6:puts("4");break;
            case 7:puts("40");break;
            case 8:puts("92");break;
            case 9:puts("352");break;
            case 10:puts("724");break;
        }
    }
	return 0;
}

这是作弊……!数组的DFS是会超时的,事先打个表就可以0ms:
#include <cstdio>
bool vis[3][25];
int n, tot, que[11];

void dfs(int cur) {
	if(cur == n)
		tot++;//每次放置到第n个皇后就是递归边界(皇后从0~n-1)
	else
		for(int i = 0; i < n; i++) {//枚举列,递归行
			if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {
				vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = true;//cur+i -> 副对角线; cur-i+n -> 主对角线
				dfs(cur+1);
				vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = false;
			}//数学知识:主对角线上的位置,b = 行 - 列 + n; 副对角线上的位置,b = 行 + 列————b是常数
		}
}

int main() {
	for(int i = 1; i <= 10; i++) {
		tot = 0;
		n = i;
		dfs(0);
		que[i] = tot;
	}
	while(~scanf("%d", &n), n) {
		printf("%d\n", que[n]);
	}
	return 0;
}

都不满意?下面才是重点

位运算解决N皇后问题——
来源:http://blog.csdn.net/kai_wei_zhang/article/details/8033194
思路:参见来源(或者看我的注释)。
代码如下:
#include<cstdio>
#include<cstring>
int n, LIM, tot, que[11];
//pos:可放置的位置表示为1; row:行、ld:左下点、rd:右下点 不可放置的位置表示为1
void dfs(int row,int ld,int rd) {//递归行,位运算列
    if(row == LIM) {//每行都有一个皇后,得到一个可行解
		tot++;
		return ;
    }
    int pos = (~(row|ld|rd)) & LIM;//(row|ld|rd)取反之后再按位与,消去前面的无效位得到棋盘上该行可放的位置:用1表示
    while(pos) {//自右向左枚举列
        int p = pos & (~pos + 1);//依次取最右边的1,也可以改写成 pos & -pos
        //若最后一位原本可行,对pos取反再加1之后,仍得到最后一位,若不可行,加1之后会一直进位,直到原本可行那一位
        dfs(row|p, (ld|p)<<1, (rd|p)>>1);
        pos -= p;//完成本层dfs之后消去所选取的位置p
    }
}

int main(){
	for(int i = 1; i <= 10; i++) {
		LIM = (1<<i) - 1;//二进制下就是i个1
        tot = 0;
        dfs(0, 0, 0);
        que[i] = tot;
	}
    while(~scanf("%d", &n), n) {
        printf("%d\n", que[n]);
    }
    return 0;
}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值