题目 1460: [蓝桥杯][基础练习VIP]2n皇后问题
题目描述
给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上。问总共有多少种放法?n小于等于8。
输入
输入的第一行为一个整数n,表示棋盘的大小。
接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。
输出
输出一个整数,表示总共有多少种放法。
样例输入
4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
2
递归思路:
// 2n皇后的迭代算法练习
// 为了弥补自己的不足,要对每一种代码掌握透彻
// 基本方法 回溯
/*
我们要明白一点的是n*n皇后都可以看成n次游戏的合成
即在满足一种皇后的放置情况下,放置第二个皇后
对于皇后问题,使用一维数组可以很容易判断是否当前位置
能够放置,详细请上网查找资料
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
int a[9]; //表示第i行放置白皇后的位置
int Map[9][9]; //保存图盘信息
int b[9]; //表示第i行放置黑皇后的信息
int n;
// 首先使用递归的思路
int sum = 0; //记录方法数
void Init()
{
for(int i =0;i<=n;i++)
{
a[i] = INF;
b[i] = INF;
}
for(int i =1;i<=n;i++)
for(int j =1;j<=n;j++)
{
cin>>Map[i][j];
}
}
bool check1(int row,int col)
{
if(Map[row][col]==0) return false;
for(int i =1;i<row;i++)
{
if(a[i]==col||abs(a[i]-col)==abs(i-row)) return false; //核心代码
}
return true;
}
bool check2(int row,int col)
{
if(Map[row][col]==0) return false;
if(a[row]==col) return false;
for(int i =1;i<row;i++)
{
if(b[i]==col||abs(b[i]-col)==abs(i-row)) return false; //核心代码
}
return true;
}
void dfs2(int row)
{
for(int i = 1;i<=n;i++)
{
if(check2(row,i))
{
b[row] = i;
if(row==n)
{
sum++;
}
else
dfs2(row+1);
}
}
return;
}
void dfs1(int row)
{
for(int i = 1;i<=n;i++)
{
if(check1(row,i))
{
a[row] = i;
if(row==n)
{
dfs2(1);
}
else
dfs1(row+1);
}
}
return;
}
int main()
{
cin>>n;
Init();
dfs1(1); //首先从白皇后开始遍历
cout<<sum<<endl;
}
使用while语句的迭代核心部分:
void queen1()
{
int row=1,col= 1;
while(row<=n)
{
while(col<=n)
{
if(check1(row,col))
{
a[row] = col;
col=1; //从头开始找
break;
}
else
col++;
}
if(a[row]==INF)
{
if(row == 1) break; //函数出口
else
{
row--;
col = a[row]+1;
a[row] = INF;
continue;
}
}
if(row == n)
{
queen2();
col = a[row]+1;
a[row] = INF;
continue;
}
row++;
}
}
进阶解法:位运算,这里由于是双皇后和有0的问题,所以暂时还没想到如何来解决。不过思想还是要清楚的。
//进阶版本,位预算,速度及其快,但重要的是理解他的本质
void test1(int row,int ld,int rd) //分别代表数列,左对角线,右对角线上的禁位
{
int pos,p;
if(row!=upperlim)
{
pos = upperlim&(~(row|ld|rd));//取他们能放的位置
while(pos)
{
p = pos&(~pos+1);//p是提取pos最右边能放的位置
pos = pos -p; //将他从可放的位置除去
test1(row|p,(ld|p)<<1,(rd|p)>>1);
}
}
else
sum++;
}