搜索问题简析

dfs搜索–数字排列、n皇后问题、棋盘挑战

本文仅是粗浅的谈谈本人对dfs的理解,很是粗浅,仅供参考

一、dfs相关概念

深度优先遍历(dfs):

感觉就是一个执着的人,一条路走到黑,直到走到尽头(这条路径走完了)或者堵住了(不满足所要求的条件)。

本质

通过递归的方式遍历图中的每一个点。

回溯

当我们在一条路走到头,往回走时,就叫回溯

剪枝

如果已经确定这条路没有我们想要的答案,就不用继续在这条路上走下去,开始走其他分支或者往回走,这样节省时间的方法叫做剪枝。

恢复现场

当我们回溯的时候,原来这个图是什么样的,我们还要变回什么样。这是一个好习惯,我们用了什么东西,就需要还回什么东西。这样做的目的: 当我们遍历完这条分支,去遍历下一条分支的时候,我们需要保证当前图其他条件的一致性,也就是遍历每一条分支的时候,当前图的状态都是一样的。保证遍历每一条分支的时候都是公平的。

二、数字排列

算是对dfs初次感受

问题描述

求n个数的全排列
代码如下:

#include <iostream>
using namespace std;
const int N = 10;
int n;
int path[N]; //状态
bool st[N];

void dfs(int u){
    if(u == n){
        for(int i = 0; i < n; i++) printf("%d ", path[i]);
        puts("");
        return;
    }
    for(int i = 1; i <= n; i++){ //求1-n这几个数字的全排列,控制
        if(!st[i]){
            path[u] = i;
            st[i] = true;  //标记所走的这条路用了i这个数字了
            dfs( u + 1);
            st[i] = false;//这条路已结束,恢复现场,把数字还原回去,让其他路还可以用
        }
    }
}

int main(){
    cin >> n;
    dfs(0); //0相当于根节点,从根节点开始,u = n时,最后一层
    return 0;
}

三、n皇后问题

问题描述

n-皇后问题是指将 n 个皇后放在 n∗n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。现在给定整数n,请你输出所有的满足条件的棋子摆法。

输入格式
共一行,包含整数n。

输出格式
每个解决方案占n行,每行输出一个长度为n的字符串,用来表示完整的棋盘状态。
其中”.”表示某一个位置的方格状态为空,”Q”表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围:1 <= n <= 9;

示例:输入4
输出:.Q..
...Q
Q...
..Q.

..Q.
Q...
...Q
.Q..

代码如下():

#include <iostream>
using namespace std;
const int N = 10;
int n;
char path[N][N]; //状态
bool col[N], dg[2 * N], udg[2 * N]; //同一列,正对角线,反对角线
//正对角线、反对角线的个数都是2*n-1

void dfs(int u){
    if(u == n){
        for(int i = 0; i < n; i++) puts(path[i]);
        puts("");
        return;
    }
    for(int i = 0; i < n; i++){
        if(!col[i] && !dg[u+i] && !udg[n-u+i]){
            path[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs( u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
            path[u][i] = '.';
        }
    }
}

int main(){
    cin >> n;
    for(int i = 0; i < n; i ++){
        for(int j = 0; j < n; j ++){
            path[i][j] = '.';
        }
    }
    dfs(0); //u = n时,最后一层
    return 0 ;
}

解释标记对角线数组的下标

对于两条对角线来说。
咱们的下标行和列都从1开始存储
d1是从左下到右上的对角线
这些点有一个共同的特征,就是对应i和j的下标之和相同
d2是从右下到左上的对角线,
这些点有一个共同的特征,就是对应i和j的下标之和+n相同,即i-j+n
所以只要标记对应的一个值,就代表这个对角线被占用了。

四,棋盘挑战

问题描述

给定一个 N×N 的棋盘,请你在上面放置 N 个棋子,要求满足:

  • 每行每列都恰好有一个棋子
  • 每条对角线上都最多只能有一个棋子
    1   2   3   4   5   6
  -------------------------
1 |   | O |   |   |   |   |
  -------------------------
2 |   |   |   | O |   |   |
  -------------------------
3 |   |   |   |   |   | O |
  -------------------------
4 | O |   |   |   |   |   |
  -------------------------
5 |   |   | O |   |   |   |
  -------------------------
6 |   |   |   |   | O |   |
  -------------------------

上图给出了当 N=6 时的一种解决方案,该方案可用序列 2 4 6 1 3 5 来描述,该序列按顺序给出了从第一行到第六行,每一行摆放的棋子所在的列的位置。
请你编写一个程序,给定一个 N×N 的棋盘以及 N 个棋子,请你找出所有满足上述条件的棋子放置方案。

输入格式
共一行,一个整数 N。
输出格式
共四行,前三行每行输出一个整数序列,用来描述一种可行放置方案,序列中的第 i 个数表示第 i 行的棋子应该摆放的列的位置。
这三行描述的方案应该是整数序列字典序排在第一、第二、第三的方案。
第四行输出一个整数,表示可行放置方案的总数。
数据范围
6≤N≤13

样例
输入 6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

注意:dfs天然保证字典序,即输出前三种方案即为所求
一般人都是正着搜,自然是保证字典序,不要为难自己非得倒着搜。。
代码如下():

#include <iostream>
using namespace std;
const int N = 15;
int n;
int path[N], ans; //存储答案,一维的答案
bool col[N], dg[2 * N], udg[2 * N]; //同一列,正对角线,反对角线
//int path[N], ans;

void dfs(int u){
    if(u == n){
        ans ++;
        if(ans <= 3){
            for(int i = 0; i < n; i++) printf("%d ", path[i]);
            puts("");
        }
        return;
    }
    for(int i = 1; i <= n; i++){
        if(!col[i] && !dg[u+i] && !udg[n - i + u]){
            path[u] = i;
            col[i] = dg[u + i] = udg[n - i + u] = true;
            dfs( u + 1);
            col[i] = dg[u + i] = udg[n + u - i] = false;
            path[u] = 0;
        }
    }
}

int main(){
    cin >> n;
    dfs(0); //u = n时,最后一层
    cout << ans << endl;
    return 0 ;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Xuhx&

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

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

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

打赏作者

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

抵扣说明:

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

余额充值