目录
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;
}