zoukankan      html  css  js  c++  java
  • 搜索--P1219 N皇后

    题目描述

    检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
    在这里插入图片描述
    上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:

    行号 1 2 3 4 5 6

    列号 2 4 6 1 3 5

    这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。

    输入输出格式

    输入格式:

    一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

    输出格式:

    前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
    在这里插入图片描述

    思路

    首先是如何描述题目,可以借助数组的索引为行,对应值为列,如此行必定不会重复,然后对列的可能的值进行全排列(这样行列均不会重复),找出可以满足对角线不平行的组合
    经典问题,使用普通的dfs搜索全部路径,也就是搜索到最底层,简单容易理解但是耗时久,优化的思路是到达新的一层直接对新入单位进行判断,满足进入下一层,不满足则结束当前路径

    TLE代码(普通dfs,耗时久)

    #include<cstdio>
    #include<cmath>
    using namespace std;
    int num = 0;//全排列数字的最大值
    int hash[14] = {0};//是否已经选择的标志,0未选,1已选
    int nums[14] = {0};//存储需要全排列的数字
    int results[14]={0};//搜索过程中记录的全排列的值
    int temp =0;//满足条件的个数
    void dfs(int depth) {
        if (depth == num+1) {
            for (int i = 1; i < num; ++i) {
                for (int j = i+1; j <= num; ++j) {
                    if(abs(results[j]-results[i])==abs(j-i)){
                        return;
                    }
                }
            }
            if(temp<3){
                for (int k = 1; k <= num; ++k) {
                    printf("%d ",results[k]);
                }
                printf("
    ");
            }
            temp++;
            return;
        }
    
        for (int j = 1; j <= num; ++j) {
            if(hash[j]==0){
                results[depth]=nums[j];
                hash[j]=1;
                dfs(depth+1);
                hash[j]=0;
            }
        }
    }
    int main() {
        scanf("%d",&num);
        for (int i = 1; i <= num; ++i) {
            nums[i]=i;
        }
        dfs(1);
        printf("%d",temp);
        return 0;
    }
    

    AC1(回溯dfs)

    #include<cstdio>
    #include<cmath>
    
    using namespace std;
    int num = 0;//全排列数字的最大值
    int hash[14] = {0};//是否已经选择的标志,0未选,1已选
    //int nums[14] = {0};//存储需要全排列的数字,可以不使用
    int results[14]={0};//搜索过程中记录的全排列的值
    int temp =0;//满足条件的个数
    //回溯版本
    void dfs(int depth) {
        //判断新入的是否满足,不满足直接回退到上一层
        //当前深度为depth,数组最大索引为depth-1,而新入的值与前面的值进行比较,所以i < depth-1
        for (int i = 1; i < depth - 1; ++i) {
            int left = abs(results[depth - 1] - results[i]);
            int rigth = abs(depth - 1 - i);
            if (left == rigth) {
                return;
            }
        }
        //若到达最后这一层,一定是满足的
        if (depth == num + 1) {
            if(temp<3){
                for (int k = 1; k <= num; ++k) {
                    printf("%d ", results[k]);
                }
                printf("
    ");
            }
            temp++;
            return;
        }
        //下一层的入口
        for (int j = 1; j <= num; ++j) {
            if (hash[j] == 0) {
                //results[depth] = nums[j];等价
    		    results[depth] = j;
                hash[j] = 1;
                dfs(depth + 1);
                hash[j] = 0;
            }
        }
    }
    int main() {
        scanf("%d", &num);
       // for (int i = 1; i <= num; ++i) {
       //     nums[i] = i;
       // }
        dfs(1);
        printf("%d", temp);
        return 0;
    }
    

    稍微优化(更直观)

    #include<cstdio>
    #include<cmath>
    using namespace std;
    int num = 0;
    int hash[14] = {0};
    int results[14] = {-1};
    int ans = 0;
    //回溯版本
    void dfs(int depth) {
        if (depth == num + 1) {
            if(ans<3){
                for (int k = 1; k <= num; ++k) {
                    printf("%d ", results[k]);
                }
                printf("
    ");
            }
            ans++;
            return;
        }
        for (int j = 1; j <= num; ++j) {
            bool flag = true;
            if (hash[j] == 0) {
                //回溯,不能取最后一个
                for (int i = 1; i < depth ; ++i) {
                    // |y1 - y2| = |x1 - x2|
                    if (abs(j - results[i]) == abs(depth - i)) {
                        flag = false;
                        break;
                    }
                }
                if(flag){
                    results[depth] = j;
                    hash[j] = 1;
                    dfs(depth + 1);
                    hash[j] = 0;
                }
            }
        }
    }
    int main() {
        scanf("%d", &num);
        dfs(1);
        printf("%d", ans);
        return 0;
    }
    

    学到的点

    1 回溯是dfs的一种优化方式
    2 理解dfs的关键在于理解栈的调用,(形象化的比喻搜索的过程,就是一层一层执行函数)

  • 相关阅读:
    react写一个todo
    react小知识2
    你可能不知道的github语法——图标
    对象和数组的浅复制和深复制
    箭头函数的嵌套
    DOMContentLoaded事件中使用异步
    react-router V4中的url参数
    如何使用react-redux——傻瓜版
    新时代的页面性能优化
    Performance面板看js加载
  • 原文地址:https://www.cnblogs.com/sunqiangstyle/p/10312276.html
Copyright © 2011-2022 走看看