N皇后问题

学习自

回溯法

定义

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

运用回溯法解题关键要素
  1. 针对给定的问题,定义问题解空间。
  2. 确定易于搜索的解空间结构。
  3. 以深度优先方式搜索解空间,并且在搜索过程中使用剪枝函数避免无效搜索。

问题描述:

在n*n的网格中放置n个皇后,使任意两个皇后都不被相互吃掉,共有多少种放置方法。规则是皇后能吃掉同一行、同一列、同一对角线上的任意棋子。

解题思路:

设任意两棋子位置为(Xi,Yi)和(Xj,Yj)

限制条件:

不在同一行 Xi!=Xj,

不在同一列|Yi!=Yj ,

不在同一对角线abs(Xi-Xj)!=abs(Yi-Yj).

通过回溯法进行遍历判断是否铆足条件,从而求出相应的解。

PS:LinkedList是采用链表作为内部数据结构,其增加、删除操作的时间复杂度都是O(1),查找和修改的时间复杂度是O(n).LinkedList的pollLast()方法用于删除尾节点,链表为空时返回null.

具体步骤:

1.通过链表方式

首先定义一个类存放点(皇后)的位置。

class Location{
        int x;//对应棋盘的行
        int y;//对应棋盘的列
        
        Location(int x,int y){
            this.x = x;
            this.y = y;
        }
        
        public String toString() {
            return "(" + x + "," + y + ")";
        }
    }
View Code

判断是否满足不在同一行、同一列、同一对角线。

/**
     * 判断位置为loc的皇后是否合法
     */
    private static boolean isLegalLoc(LinkedList<Location> list, Location loc) {
        for(Location each : list){
            if(loc.x == each.x || loc.y == each.y)  //判断是否在同一行或同一列
                return false;
            else if (Math.abs(loc.x - each.x) == Math.abs(loc.y - each.y))  //判断是否在同斜线上
                return false;
        }
        return true;
    }
View Code

放置方式(回溯算法)

/**
     * 主要函数,用回溯法。
     */
    private static void NQueen(LinkedList<Location> list, int x, int y) {   
 
        if(list.size() == SIZE){  //当list元素个数为SIZE时,表示SIZE个皇后都摆放完毕,打印后即可退出函数。
            printLocation(list);  //打印皇后摆放方式
            return ;
        }
 
        for(int i = x ; i < SIZE ; i++){
            Location loc = new Location(i, y);
            if(isLegalLoc(list, loc)){
                list.add(loc);  //将第y行的皇后摆放好
                NQueen(list, 0, y+1);  //开始摆放y+1行的皇后,同样从第0列开始摆放
                list.pollLast();  //每次摆放完一个皇后后,都要将其撤回,再试探其它的摆法。
            }                   
        }           
    }
View Code

完整代码:

package test;

import java.util.LinkedList;
import java.util.Scanner;

public class N_quene1 {
    private static int SIZE = 0;//皇后的个数
    private static int count = 0;//记录摆放的方式数
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner input = new Scanner(System.in);
        System.out.println("请输入你要解决几个皇后的问题");
        SIZE = input.nextInt();
        input.close();
         LinkedList<Location> list = new LinkedList<Location>();
         NQueen(list, 0, 0);  //从棋盘的第0行第0列开始
         System.out.println(SIZE + "皇后共有 " + count + "种摆放方式");
 
    }
    static class Location{
        int x;//对应棋盘的行
        int y;//对应棋盘的列
        
        Location(int x,int y){
            this.x = x;
            this.y = y;
        }
        
        public String toString() {
            return "(" + x + "," + y + ")";
        }
    }
    
    /**
     * 主要函数,用回溯法。
     */
    private static void NQueen(LinkedList<Location> list, int x, int y) {   
 
        if(list.size() == SIZE){  //当list元素个数为SIZE时,表示SIZE个皇后都摆放完毕,打印后即可退出函数。
            printLocation(list);  //打印皇后摆放方式
            return ;
        }
 
        for(int i = x ; i < SIZE ; i++){
            Location loc = new Location(i, y);
            if(isLegalLoc(list, loc)){
                list.add(loc);  //将第y行的皇后摆放好
                NQueen(list, 0, y+1);  //开始摆放y+1行的皇后,同样从第0列开始摆放
                list.pollLast();  //每次摆放完一个皇后后,都要将其撤回,再试探其它的摆法。
            }                   
        }           
    }
 
    
    /**
     * 判断位置为loc的皇后是否合法
     */
    private static boolean isLegalLoc(LinkedList<Location> list, Location loc) {
        for(Location each : list){
            if(loc.x == each.x || loc.y == each.y)  //判断是否在同一行或同一列
                return false;
            else if (Math.abs(loc.x - each.x) == Math.abs(loc.y - each.y))  //判断是否在同斜线上
                return false;
        }
        return true;
    }
 
    /**
     * 打印皇后摆放方式
     * @param list
     */
    private static void printLocation(LinkedList<Location> list) {
        String[][] show = new String[SIZE][SIZE];
        for(int i = 0;i<SIZE;i++) {
            for(int j = 0;j<SIZE;j++) {
                show[i][j] = "0";
            }
        }
        for(Location each : list){
            System.out.print(each.toString() + "\t");
            show[each.x][each.y] = "1";
        }
        System.out.println();
        
        for(int i =0;i<SIZE;i++) {
            for(int j=0;j<SIZE;j++) {
                System.out.print(show[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
 
        count ++;
    }
 
}
View Code
 2.通过数组方式
public static void tria(int[] arr, int i, int n) {
        if(i >= n) {
            ++count;
        } else {
            for(int j = 0; j < n; j++) {
                arr[i] = j;
                if(place(arr, i)) {
                    tria(arr, i+1, n);
                }
            }
        }
    }
View Code

 

总结

根据思想自己敲了一遍,算是加深自己的记忆程度吧。

转载于:https://www.cnblogs.com/fenggedainifei/p/10543380.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值