DFS - 排列数字 + n-皇后问题
1、排列数字
给定一个整数n,将数字1~n排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数n。
输出格式
按字典序输出所有排列方案,每个方案占一行。
数据范围
1≤n≤7
输入样例:
3
输出样例:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
分析:
依 次 枚 举 n 位 数 的 每 一 位 u , 对 每 一 位 还 要 依 次 枚 举 [ 1 , n ] 的 所 有 数 字 , 若 当 前 数 字 还 未 被 使 用 过 , 就 将 该 数 字 填 入 第 u 位 上 。 若 已 经 枚 举 了 n 位 , 就 将 结 果 输 出 , 结 束 递 归 。 依次枚举n位数的每一位u,对每一位还要依次枚举[1,n]的所有数字,\\若当前数字还未被使用过,就将该数字填入第u位上。\\若已经枚举了n位,就将结果输出,结束递归。 依次枚举n位数的每一位u,对每一位还要依次枚举[1,n]的所有数字,若当前数字还未被使用过,就将该数字填入第u位上。若已经枚举了n位,就将结果输出,结束递归。
根
据
上
述
思
路
能
够
画
出
一
颗
递
归
树
。
以
样
例
为
例
,
作
出
下
图
:
根据上述思路能够画出一颗递归树。\\以样例为例,作出下图:
根据上述思路能够画出一颗递归树。以样例为例,作出下图:
递
归
的
顺
寻
是
从
左
到
右
的
。
递归的顺寻是从左到右的。
递归的顺寻是从左到右的。
因 为 每 次 都 是 直 接 递 归 到 底 , 就 是 相 当 于 枚 举 完 一 个 排 列 数 , 因 此 我 们 可 以 用 一 个 数 组 来 记 录 本 次 枚 举 的 排 列 数 的 每 一 位 放 的 什 么 数 字 , 再 用 一 个 数 组 标 记 当 前 已 经 使 用 了 哪 些 数 字 。 因为每次都是直接递归到底,就是相当于枚举完一个排列数,\\因此我们可以用一个数组来记录本次枚举的排列数的每一位放的什么数字,再用一个数组标记当前已经使用了哪些数字。 因为每次都是直接递归到底,就是相当于枚举完一个排列数,因此我们可以用一个数组来记录本次枚举的排列数的每一位放的什么数字,再用一个数组标记当前已经使用了哪些数字。
具体落实:
① 、 用 一 个 p a t h 数 组 记 录 每 次 枚 举 的 方 案 , s t 数 组 来 标 记 正 在 枚 举 的 排 列 数 中 有 哪 些 数 字 已 经 被 使 用 过 了 。 ①、用一个path数组记录每次枚举的方案,st数组来标记正在枚举的排列数中有哪些数字已经被使用过了。 ①、用一个path数组记录每次枚举的方案,st数组来标记正在枚举的排列数中有哪些数字已经被使用过了。
② 、 d f s 枚 举 排 列 数 的 第 u 位 放 什 么 数 字 , 若 已 经 放 了 n 位 , 就 输 出 记 录 的 答 案 , 结 束 递 归 。 否 则 就 依 次 枚 举 放 哪 个 数 字 , 若 当 前 数 字 i 在 本 次 递 归 还 未 使 用 过 , 就 将 其 放 在 第 u 位 , 同 时 标 记 这 个 数 字 。 注 意 , 枚 举 完 一 种 排 列 数 后 , 要 依 次 将 之 前 标 记 过 的 数 字 取 消 标 记 , 这 个 过 程 是 回 溯 的 过 程 。 ②、dfs枚举排列数的第u位放什么数字,若已经放了n位,就输出记录的答案,结束递归。\\\qquad否则就依次枚举放哪个数字,若当前数字i在本次递归还未使用过,就将其放在第u位,同时标记这个数字。\\\qquad注意,枚举完一种排列数后,要依次将之前标记过的数字取消标记,这个过程是回溯的过程。 ②、dfs枚举排列数的第u位放什么数字,若已经放了n位,就输出记录的答案,结束递归。否则就依次枚举放哪个数字,若当前数字i在本次递归还未使用过,就将其放在第u位,同时标记这个数字。注意,枚举完一种排列数后,要依次将之前标记过的数字取消标记,这个过程是回溯的过程。
代码:
#include<iostream>
using namespace std;
const int N=10;
int n,path[N];
bool st[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++) cout<<path[i]<<' ';
cout<<endl;
return ;
}
for(int i=1;i<=n;i++)
if(!st[i])
{
path[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
2、n-皇后问题
n-皇后问题是指将 n 个皇后放在 n∗n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数n,请你输出所有的满足条件的棋子摆法。
输入格式
共一行,包含整数n。
输出格式
每个解决方案占n行,每行输出一个长度为n的字符串,用来表示完整的棋盘状态。
其中”.”表示某一个位置的方格状态为空,”Q”表示某一个位置的方格上摆着皇后。
每个方案输出完成后,输出一个空行。
输出方案的顺序任意,只要不重复且没有遗漏即可。
数据范围
1≤n≤9
输入样例:
4
输出样例:
.Q..
...Q
Q...
..Q.
..Q.
Q...
...Q
.Q..
分析:
每 行 只 放 一 个 皇 后 , 每 行 能 够 放 的 位 置 有 限 , n 行 可 以 视 作 一 个 n 位 数 的 排 列 , 每 一 位 枚 举 当 前 行 哪 些 列 能 够 摆 放 皇 后 , 即 有 哪 些 数 字 还 没 有 被 使 用 过 。 每行只放一个皇后,每行能够放的位置有限,\\n行可以视作一个n位数的排列,每一位枚举当前行哪些列能够摆放皇后,即有哪些数字还没有被使用过。 每行只放一个皇后,每行能够放的位置有限,n行可以视作一个n位数的排列,每一位枚举当前行哪些列能够摆放皇后,即有哪些数字还没有被使用过。
转 化 为 与 第 一 题 排 列 数 字 相 似 的 问 题 。 转化为与第一题排列数字相似的问题。 转化为与第一题排列数字相似的问题。
不
同
的
是
,
这
里
需
要
用
3
个
数
组
来
标
记
,
列
、
正
对
角
线
、
反
对
角
线
。
不同的是,这里需要用3个数组来标记,列、正对角线、反对角线。
不同的是,这里需要用3个数组来标记,列、正对角线、反对角线。
它
们
之
间
的
坐
标
关
系
如
下
:
对
坐
标
为
(
u
,
i
)
的
位
置
,
其
正
对
角
线
的
下
标
为
u
+
i
,
反
对
角
线
的
下
标
为
n
−
u
+
i
。
它们之间的坐标关系如下:\\对坐标为(u,i)的位置,其正对角线的下标为u+i,反对角线的下标为n-u+i。
它们之间的坐标关系如下:对坐标为(u,i)的位置,其正对角线的下标为u+i,反对角线的下标为n−u+i。
代码:
#include<iostream>
#include<cstring>
using namespace std;
const int N=10;
int n;
char g[N][N];
bool col[N],dg[N],udg[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<n;i++) puts(g[i]);
puts("");
return ;
}
for(int i=0;i<n;i++)
if(!col[i] && !dg[u+i] && !udg[n-u+i])
{
g[u][i]='Q';
col[i]=dg[u+i]=udg[n-u+i]=true;
dfs(u+1);
g[u][i]='.';
col[i]=dg[u+i]=udg[n-u+i]=false;
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
g[i][j]='.';
dfs(0);
return 0;
}