递归算法应用实例------八皇后算法

八皇后问题

回溯算法的经典案例

回溯算法的典型案例。该问题是国际西洋棋棋手马克斯.贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能外干同一行、同一列或同一斜线上,问有多少种摆法。

思路分析

  1. 第一个皇后先放在第一行第一列
  2. 第二个皇后放在第二行第一列、然后判断是否0K,如果不OK, 继续放在第二列、第三列、依次把所有列都放完,找到一个合适
  3. 继续第三个皇后,还晕第一列、第二列…直到第8个皇后也能放在一个不冲突的位置,算是找到了一个正确解
  4. 当得到一个正确解时,在栈回退到上一个栈时,就会开始回溯,即将第一个皇后,放到第一列的所有正确解,全部得到
  5. 然后回头继续第-一个皇后放第二列,后面继续循环执行1,2,3的步骤

**说明:**理论上应该创建一个二维数组来表示棋盘,但是实际上可以通过算法用一个一维数组即可解决问题. arr[8]={0,4,7,5,2,6, 1,3}表示一个正解

对应arr下标表示第几行,即第几个皇后,arr[i]=val , val表示第i+1个皇后,放在第i+1行的第val+1列

效率可能比较低,后面也有算法对其进行优化,比如贪心算法等,后面的笔记会提到

代码

package com.wang.Recursion;
/**
 * @author 王庆华
 * @version 1.0
 * @date 2020/12/20 19:45
 * @Description TODO
 * @pojectname 八皇后问题
 */
public class Queue8 {
    //定义一个max表示共有多少个皇后
    int max = 8;
    //定义数组array,保存皇后放置的位置,比如arr[8]={0,4,7,5,2,6, 1,3}
    int[] array = new int[max];
    static int count = 0;
    public static void main(String[] args) {
        //测试
        Queue8 queue8 = new Queue8();
        queue8.check(0);
        System.out.println("一共有"+count+"次解法");
    }
    //编写一个方法,放置第n个皇后
    //特别注意:check是每一层递归时,进入到check都有for循环,因此会有回溯
    private void check(int n){
        //如果n = max,皇后放完了,
        if (n == max){
            print();
            return;
        }
        //没有放完的话,依次放入皇后,判断是否冲突
        for (int i = 0; i < max ; i++) {
            //先把当前的皇后n,放到改行的第一列
            array[n] = i;
            //判断当放置了第n个皇后到i列时,是否冲突
            if (judge(n)) {
                //不冲突,接着放n+1个皇后,即开始递归
                check(n+1);
            }
            //如果冲突,就继续执行array[n] = i;这个时候i已经++了,就是放到了下一列
        }
    }

    /**
     //查看当我们放置第n个皇后时,就去检测该皇后是否和前面已经摆好的皇后冲突
     * @param n 表示第n个皇后
     * @return
     */
    private boolean judge(int n){
        for (int i = 0; i <n ; i++) {
            //array[i] == array[n]表示第n个皇后是否和前面i个皇后是否在同一列
            //Math.abs(n-i) == Math.abs(array[n] - array[i]表示第n个皇后是否和第i个皇后在同一斜线
            if (array[i] == array[n] ||Math.abs(n-i) == Math.abs(array[n] - array[i] )){
                return false;
            }
        }
        return true;
    }

    //将皇后拜访的位置打印出来
    private void print(){
        count++;
        for (int i = 0; i <array.length ; i++) {
            System.out.print(array[i]+"");
        }
        System.out.println();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
#include "stdafx.h" #include<stdio.h> #define NUM 8 //定义数组大小 int main () { int a[NUM+1 ]; //int number; int i; int k; int flag; int notfinish = 1; int count = 0; i = 1; //正在处理的元素下标,表示前i-个元素已符合要求,正在处理第i个元素 a[1] = 1; //为数组的第一个元素赋初值 printf ("结果:\n"); while (notfinish) //处理尚未结束 { while (notfinish && i <= NUM) //处理尚未结束且还没处理到第NUM个元素 { for (flag = 1, k = 1; flag && k < i; k++) //判断是否有多个皇后在同一行 { if (a[k] == a[i]) flag = 0; } for (k = 1; flag && k < i; k++) //判断是否有多个皇后在同一对角线 { if ((a[i] == a[k] - (k - i)) || (a[i] == a[k] + (k - i))) flag = 0; } if (!flag) //若存在矛盾不满足要求,需要重新设置第i个元素 { if (a[i] == a[i - 1]) //若a[i]的值已经经过一圈追上a[i-]的值 { i--; //退回一步,重新试探处理前的一个元素 if (i > 1 && a[i] == NUM) { a[i] = 1; //当a[i]的值为NUM时将a[i]的值置 } else if (i == 1 && a[i] == NUM) { notfinish = 0; //当第一位的值达到NUM时结束 } else { a[i]++; //将a[i]的值取下一个值 } } else if (a[i] == NUM) { a[i] = 1; } else { a[i]++; //将a[i]的值取下一个值 } } else if (++i <= NUM) //第i位已经满足要求则处理第i+位 { if (a[i - 1] == NUM) //若前一个元素的值为NUM则a[i]= { a[i] = 1; } else { a[i] = a[i - 1] + 1; //否则元素的值为前一个元素的下一个值 } } } if (notfinish) { ++count; printf ((count - 1) % 3 ? "[%2d]:" : "[%2d]:", count); for (k = 1; k <=NUM; k++) //输出结果 { printf (" %d", a[k]); } printf (" \n"); if (a[NUM - 1] < NUM) //修改倒数第二位的值 { a[NUM - 1]++; } else { a[NUM - 1] = 1; } i = NUM - 1; //开始寻找下一个满足条件的解 } }//while return 0; }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会写代码的花城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值