思路:
注意以下几点:恢复现场,剪枝
可以画一个递归搜索树来帮助理解(以排列组合为例)
搜索过程如下:
Acwing842. 排列数字
//深度优先搜索dfs
#include <iostream>
using namespace std;
const int N = 10;
int path[N], n;//path[]表示路径
bool st[N];//表示状态,全局变量初始化为false
void dfs(int u)
{
if (u == n)
{
for (int i = 0; i < n; i ++ ) printf("%d ",path[i]);
putchar('\n');
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()
{
scanf("%d",&n);
dfs(0);
return 0;
}
AcWing 843. n-皇后问题
可以画递归搜索树进行模拟便于理解,然后此题需注意剪枝
第一种解法(排列组合的思想):
按行遍历,即u代表的是层数
#include <iostream>
using namespace std;
const int N = 20;
int n;
char str[N][N];
bool col[N], dg[N], udg[N];//列,对角线,反对角线
void dfs(int u)
{
if (u == n)
{
for (int i = 0; i < n; i ++ ) puts(str[i]);
puts("");
return;
}
for (int i = 0; i < n; i ++ )
{
if (!(col[i]) && (!dg[u + i]) && (!udg[u - i + n]))
{
str[u][i] = 'Q';
col[i] = dg[i + u] = udg[u - i + n] = true;//标记不可填
dfs(u + 1);//递归下一层
col[i] = dg[i + u] = udg[u - i + n] = false;//恢复现场
str[u][i] = '.';
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
for (int j = 0; j < n; j ++ )
str[i][j] = '.';
dfs(0);
return 0;
}
第二种解法(时间比上一种要慢):
对每个格子进行遍历,每个格子有两种选择即放皇后或者不放皇后
x代表行,y代表列
对于每一行挨个判断放不放皇后
#include <iostream>
using namespace std;
const int N = 20;
int n;
char str[N][N];
bool row[N], col[N], dg[N], udg[N];
void dfs(int x, int y, int s)
{
if (y == n) y = 0, x ++;//如果列越界了,就跳到下一行的第一个位置,即行数加一,列数归零
if (x == n)//行全部遍历完成
{
if (s == n)//如果n个皇后全部放入棋盘,即找到了一种情况
{
for (int i = 0; i < n; i ++ ) puts(str[i]);
puts("");
}
return;
}
//不放皇后->当前行遍历到的列数向前走
dfs(x, y + 1, s);
//放皇后
if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])//满足放皇后的要求
{
str[x][y] = 'Q';//放上皇后
row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;//该点放上皇后了,标记一下
dfs(x, y + 1, s + 1);//递归到下一列,并且放了一个皇后了
//恢复现场
row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
str[x][y] = '.';
}
}
int main()
{
scanf("%d", &n );
for (int i = 0; i < n; i ++ )
{
for (int j = 0; j < n; j ++)
{
str[i][j] = '.';
}
}
dfs(0, 0, 0);
return 0;
}
排列组合总结
指数型 (任意挑选数字)
Acwing 递归实现指数型枚举
递归搜索树:
板子:
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i ++)
if (st[i]) cout << i << ' ';
cout << endl;
return;
}
//选当前数字
st[u] = true;
dfs(u + 1);
//不选当前数
st[u] = false;
dfs(u + 1);
}
全部代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int N = 20;
int n;
bool st[N];
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i ++)
if (st[i]) cout << i << ' ';
cout << endl;
return;
}
//选当前数字
st[u] = true;
dfs(u + 1);
//不选当前数
st[u] = false;
dfs(u + 1);
}
int main()
{
scanf("%d", &n);
//枚举每一个数选或不选
dfs(1);
return 0;
}
排列型(全排列)
Acwing 94.递归实现排列型枚举
递归搜索树:
板子:
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i ++) printf("%d ", path[i]);
puts("");
return;
}
for (int i = 1; i <= n; i ++)
{
if (!st[i])
{
path[u] = i;
st[i] = true;
dfs(u + 1);
st[i] = false; //恢复现场
}
}
}
全部代码:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 10;
int n;
bool st[N];
int path[N];
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i ++) printf("%d ", path[i]);
puts("");
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()
{
scanf("%d", &n);
dfs(1);
return 0;
}
组合型(选k个数按升序排列)
Acwing 93.递归实现组合型枚举
递归搜索树:
板子:
void dfs(int u, int last)
{
if (n - last < m - u) return; //剪枝
if (u > m)
{
for (int i = 1; i <= m; i ++) printf("%d ", path[i]);
puts("");
return;
}
for (int i = last; i <= n; i ++)
{
if (!st[i])
{
path[u] = i;
st[i] = true;
dfs(u + 1, i);
st[i] = false;
}
}
}
全部代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50;
int n, m;
bool st[N];
int path[N];
void dfs(int u, int last)
{
if (n - last < m - u) return; //剪枝
if (u > m)
{
for (int i = 1; i <= m; i ++) printf("%d ", path[i]);
puts("");
return;
}
for (int i = last; i <= n; i ++)
{
if (!st[i])
{
path[u] = i;
st[i] = true;
dfs(u + 1, i);
st[i] = false;
}
}
}
int main()
{
scanf("%d %d", &n, &m);
dfs(1, 1);
return 0;
}