DFS复习(八皇后、数独)

 

目录

1.八皇后问题

输入描述

输出描述

优化

2.马踏棋盘

输入描述:

输出描述:

3.数独挑战

题目描述

输入描述:

输出描述:


1.八皇后问题

在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。

输入描述

(无)

输出描述

按给定顺序和格式输出所有八皇后问题的解(见样例)。

用例输出 1 

No. 1
1 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 0 
0 0 0 0 1 0 0 0 
0 0 0 0 0 0 0 1 
0 1 0 0 0 0 0 0 
0 0 0 1 0 0 0 0 
0 0 0 0 0 1 0 0 
0 0 1 0 0 0 0 0 

 简单的dfs入门

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n=8,ans=0;        //ans记录解个数
int a[10],visit[10];    //a[i]存放第i行皇后位置,visit[i]存放第i列是否有皇后
void dfs(int dep){
    if(dep>n){        //前n行全部放好
        printf("No.%d\n",++ans);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i]==j)cout<<1<<' ';
                else cout<<0<<' ';
            }
            cout<<endl;
        }
        return;
    }
    for(int i=1;i<=n;i++){
        if(visit[i])continue;    //第i列有皇后,搜下一列
        bool flag=0;
        for(int j=1;j<dep;j++){
            if(abs(dep-j)==abs(i-a[j])){
                flag=1;    //判断对角线,横纵坐标差值的绝对值是否相等
                continue;
            }
        }
        if(flag)continue;
        visit[i]=1,a[dep]=i;    //可以放就标记
        dfs(dep+1);
        visit[i]=0;    //回溯,visit清零
    }

}
int main(){
    dfs(1);    //从第一行开始放
    return 0;
}

优化

由于每次判断对角线需要遍历之前的所有皇后,我们可以进行优化

通过对每个对角线标号,直接判断所在对角线是否有皇后即可
找规律发现,正对角线上的横纵坐标之差是一定的,反对角线横纵坐标之和是一定的
由于坐标差可能为负,给正对角线统一加n,代码如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n=8,ans=0;
int a[10],visit[10];
int zd[30],fd[30];      //给每个正反对角线标号
void dfs(int dep){
    if(dep>n){
        printf("No.%d\n",++ans);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(a[i]==j)cout<<1<<' ';
                else cout<<0<<' ';
            }
            cout<<endl;
        }
        return;
    }
    for(int i=1;i<=n;i++){
        if(visit[i]||zd[i-dep+n]||fd[i+dep])continue;    //判断列和对角线有无皇后
        zd[i-dep+n]=1;    //标记
        fd[i+dep]=1;
        visit[i]=1,a[dep]=i;
        dfs(dep+1);
        visit[i]=0;        //回溯
        zd[i-dep+n]=0;
        fd[i+dep]=0;
    }

}
int main(){
    dfs(1);
    return 0;
}

2.马踏棋盘

https://ac.nowcoder.com/acm/problem/235814

在n行m列的棋盘上有一个中国象棋的马,马走日字且不能向左走,设原本坐标为(x,y),走一步可以达到的位置有(x+1,y+2),(x+1,y−2),(x+2,y+1),(x+2,y−1),并且不能走出棋盘。请找到可行路径的条数,使得马从棋盘的左下角(1,1)(1,1)(1,1)走到右上角(n,m)。

输入描述:

一行,两个整数m,n(1≤n,m≤15),表示棋盘的大小。

输出描述:

输出一行一个整数,表示马从左下角到右上角的不同路径数。

输入

4 5

输出

1

 很简单的深搜,把下一步位置枚举出来,如果合法则继续往下搜

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int ans=0;
int n,m;
bool visit[20][20];
int d[4][2]={1,2,1,-2,2,1,2,-1};    //存下一步的位置
void dfs(int x,int y){
    if(x==n&&y==m){
        ans++;
        return;
    }
    for(int i=0;i<4;i++){        //标准深搜
        int dx=x+d[i][0],dy=y+d[i][1];
        if(dx>n||dy>m||dy<1)continue;
        dfs(dx,dy);
    }
}
int main(){
    cin>>m>>n;
    dfs(1,1);
    cout<<ans;
    return 0;
}


3.数独挑战

https://ac.nowcoder.com/acm/problem/24911

题目描述

数独是一种填数字游戏,英文名叫 Sudoku,起源于瑞士,上世纪 70 年代由美国一家数学逻辑游戏杂志首先发表,名为 Number Place,后在日本流行,1984 年将 Sudoku 命名为数独,即 “独立的数字” 的缩写,意思是 “在每一格只有一个数字”。

2004 年,曾任中国香港高等法院法官的高乐德 (Wayne Gould) 把这款游戏带到英国,成为英国流行的数学智力拼图游戏。

玩家需要根据 9×9 盘面上的已知数字,推理出所有剩余位置的数字,并满足每一行、每一列、每一个粗线九宫格内的数字包含有 1-9 的数字,且不重复。

现在给你一个数独,请你解答出来。每个数独保证有且只有一个解。

输入描述:

输入仅一组数据,共 9 行 9 列,表示初始数独(其中 0 表示数独中的空位)。

输出描述:

输出共 9 行 9 列,表示数独的解。

注意⾏末没有空格。

示例1

输入

5 3 0 0 7 0 0 0 0
6 0 0 1 9 5 0 0 0
0 9 8 0 0 0 0 6 0
8 0 0 0 6 0 0 0 3
4 0 0 8 0 3 0 0 1
7 0 0 0 2 0 0 0 6
0 6 0 0 0 0 2 8 0
0 0 0 4 1 9 0 0 5
0 0 0 0 8 0 0 7 9

输出

5 3 4 6 7 8 9 1 2
6 7 2 1 9 5 3 4 8
1 9 8 3 4 2 5 6 7
8 5 9 7 6 1 4 2 3
4 2 6 8 5 3 7 9 1
7 1 3 9 2 4 8 5 6
9 6 1 5 3 7 2 8 4
2 8 7 4 1 9 6 3 5
3 4 5 2 8 6 1 7 9

 数独也是经典的dfs模板题,思路为,把用过的数在对应的行、列、单元格标号,把空着的格所有能用的数搜一遍,填不满就回溯
单元格的标记有一个小窍门,每一个小单元内x/3*3+y/3的值是相同的,类似的N*M矩阵标号都可以标为x*M+y

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[20][20];
bool flag;
bool h[10][10],l[10][10],dyg[10][10];    //判断每行可用的数
void dfs(int x,int y){
    if(x==9){        //数独已经填满
        for(int i=0;i<9;i++){
            for(int j=0;j<9;j++){
                cout<<a[i][j]<<' ';
            }
            cout<<endl;
        }
        cout<<endl;
        return;
    }
    if(a[x][y]!=0){    //数独本身有数,搜下一位
        if(y<8)dfs(x,y+1);    //按行搜,搜完一行搜下一行
        else dfs(x+1,0);
    }
    else{
        for(int i=1;i<=9;i++){
            if((h[x][i]==1)||(l[y][i]==1)||(dyg[x/3*3+y/3][i]==1)){
                continue;    //被标记过的数不用
            }
            h[x][i]=1,l[y][i]=1,dyg[x/3*3+y/3][i]=1;
            a[x][y]=i;
            if(y<8)dfs(x,y+1);
            else dfs(x+1,0);
            a[x][y]=0;    //回溯,清零
            h[x][i]=0,l[y][i]=0,dyg[x/3*3+y/3][i]=0;
        }
    }
}
int main(){
    for(int i=0;i<9;i++){
        for(int j=0;j<9;j++){
            cin>>a[i][j];
            if(a[i][j]!=0){    //输入不为0,对行列进行标记
                int t=a[i][j];
                h[i][t]=1;
                l[j][t]=1;
                dyg[i/3*3+j/3][t]=1;
            }
        }
    }
    dfs(0,0);
    return 0;
}

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Auroraaaaaaaaaaaaa

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

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

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

打赏作者

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

抵扣说明:

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

余额充值