其实算法并不难:2n皇后问题解析(java)

    算法,通俗来讲就是解决问题的方法,不过算法要解决的问题是具有一定难度且复杂的问题。

    我认为,求解一个算法问题,要有三个步骤:问题抽象、算法设计、代码实现。

    首先是对问题的抽象,我们需要仔细阅读题目要求,要透过问题去看本质,要将问题去复杂化、结构化,这样才能有效且正确的设计出算法。

    其次是算法设计,在第一步骤的基础下,我们已经知道要干什么了,接下来就是要怎么干。解决问题的方法有很多,但有效且便利的方法需要我们根据问题情况去合情挑选和改良,如何设计出最优算法这取决于99%平日各种算法的积累(我觉得起码要能掌握数据结构的所有算法和一些常用算法:例如回溯)和1%的灵光乍现。另外不管算法如何,逻辑是一定要清晰的,否则在下一步代码实现的时候会让你痛不欲生的。

    最后是代码实现。这一部分是最能考验一个人的功底。算法设计的再巧妙,如果实现不了也是纸上谈兵。代码实现不只是要会写代码,还需要有严谨的逻辑能力,要考虑到题目要求中的所有情况,以及调试代码修改程序的能力和耐心(这取决与你的算法设计和代码实现)。

    综合以上,让我们看看这道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

样例输入

4

1 0 1 1

1 1 1 1

1 1 1 1

1 1 1 1

样例输出

0

注:以下是其他组测试数据:

input 1

3
1 1 0 
1 1 1 
1 1 0 

output1

0

input2

4
1 1 1 1 
1 0 1 1 
1 1 1 1 
1 1 1 1 

output2

2

input 3

5
1 1 1 1 1 
1 0 1 1 1 
1 1 1 1 1 
1 0 1 1 1 
1 1 1 1 1 

output3

12

input4

6
1 1 1 1 1 1 
1 1 1 1 1 1 
1 1 1 1 1 1 
1 1 1 1 1 1 
1 1 1 1 1 1 
1 1 1 1 1 1 

output4

12

input5

7
1 1 1 1 1 1 0 
1 1 1 1 1 1 1 
1 1 1 1 1 1 1 
1 1 1 1 1 1 1 
1 1 1 1 1 1 1 
1 1 1 1 1 1 1 
1 1 1 1 1 1 1 

output5

408


   一、问题抽象

        简单来看,给定一个数n,然后在n*n的方格里放2n个皇后,然后每个皇后的位置都有限定,问:有多少种放法?

首先我们把棋盘看成一个二维数组,二维数组中有些位置不能放皇后(能放的就是1,不能的是0),那么我们可以给白皇后取个名字(数字2),黑皇后也取个名字(数字3),那么我们就可以把问题抽象:在一个指定大小的二维数组中如何按照要求填放n个2和n个3?

    二、算法设计

        由第一步来看,问题最关键的地方其实在于如何合理的摆放这些2和3?其实也就是合理选择二维数组下标来摆放皇后。最直接有效的方法就是一个一个试,把所有的位置(情况)都试一遍。那么关键来了,这种思想其实就是回溯:从上往下,把满足条件的所有情况都试一遍,如果不行的话就返回(回溯)到上一层,接着试,最终把所有的情况都遍历了一边。于是问题就求解出来了,来看看我的算法设计吧:

        (1)、创建棋盘:输入n,创建n*n的二维数组,给定数组内容(哪些位置能放皇后,哪些不能放),定义sum变量记录放法数目

        (2)、判断位置合法性:由于题目要求对皇后位置较为复杂,我单独创建一个方法,用于判断每次皇后所放位置是否合法,这是回溯算法的基础。

         (3)、递归回溯:2n个皇后,我用了2n层递归,每层递归放一个皇后,在2n+1层设出口(合法放法)。每个皇后的列位置都从0到n-1之间选择,行下标可由递归层数表达,在每层递归(皇后放置)结束后进行清空该棋盘位置的皇后(令2或3变为0),这样我就把所有情况都筛选出来了。

        三、代码实现

            看了算法设计,是不是感觉很简单?不,最难的就在代码实现,我写的时候用了半个小时,不过调试代码用时很少(当时没把黑白皇后标识出来,结果运行错误),这道题的代码实现难在递归和回溯,如果你对二者都掌握不熟练的话,这道题确实有点难度,回溯可以从网上翻阅资料,有很多大佬的详细讲解,但是递归,我个人觉得是比较难理解和运用的。废话不说,下面是我的代码实现:

import java.util.*;
class test
{
static int [ ] x [ ];//二维数组用于创建棋盘
static int sum=0;//用于记录放法数目
static int n;//棋盘的大小
public static void main(String args [ ] )//创建棋盘
{
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
x=new int[n][n];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
x[i][j]=sc.nextInt();
}
DFS(0);
System.out.println(sum);
}
public static void DFS(int a)//回溯
{
if(a==2*n)
{
sum++;
return ;
}
int bj;
if(a<n)
bj=2;
else 
bj=3;
for(int i=0;i<x.length;i++)
{
if(pd(a,i)==true)
{
x[a%x.length][i]=bj;
DFS(a+1);
x[a%x.length][i]=1;//回溯
}
}
return ;
}
public static boolean pd(int a,int i)//判断皇后摆放位置合法性
{
int bj;
if(a<n)
bj=2;
else
bj=3;
if(x[a%x.length][i]!=1)
return false;
for(int j=0;j<a%x.length;j++)
if(x[j][i]==bj)
return false;
int s=i;
for(int w=a%n-1;w>=0;w--,s--)
if(s-1>=0&&x[w][s-1]==bj)
return false;
s=i;
for(int w=a%n-1;w>=0;w--,s++  )
if(s+1<n&&x[w][s+1]==bj)
return false;
return true;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值