目录
1.dfs全排列深度优先算法思路导图
2.dfs递归思想
3.主旨展现
4.详解手动模拟
5.例题来喽
5.1例题(1)来喽——递归实现排列型枚举
5.2例题(2)来喽——递归实现指数型枚举
5.3例题(3)来喽——递归实现组合型枚举
5.4例题(4)来喽——马走日
5.5例题(5)来喽——开题顺序
5.6例题(6)来喽——找素数
此图来自AC中的Hasity作者,万分感谢;
时间复杂度为 O(n*n!);空间复杂度为 O(n);
题目描述
给定一个整数 m,将数字 1∼m 排成一排,将会有很多种排列方法。
现在,请你按照字典序将所有的排列方法输出。
输入格式
共一行,包含一个整数 m。
输出格式
按字典序输出所有排列方案,每个方案占一行。
输入样例:
3
输出样例:
1 2 3 1 3 2 2 1 3 2 3 1 3 1 2 3 2 1
#include
#include
#include
using namespace std;
const int N = 20;
int a[N], m;
bool b[N];
void dfs(int x)
{
if (x == m)
{
for (int i = 0; i < m; i++)
{
cout << a[i] << " ";
}
cout << endl;
return;//回溯
}
for (int i = 1; i <= m; i++)
{
if (b[i] != true)
{
a[x] = i;
b[i] = true;
dfs(x + 1);
b[i] = false;
}
}
return;//个人感觉加上更容易理解,目的是回溯
}
int main()
{
cin >> m;
dfs(0);
return 0;
}
题目描述
打印n个数中的m个数的全排列
样例输入
5 2
样例输出
1 2 1 3 1 4 1 5 2 1 2 3 2 4 2 5 3 1 3 2 3 4 3 5 4 1 4 2 4 3 4 5 5 1 5 2 5 3 5 4
#include
#include
#include
using namespace std;
const int N = 20;
int a[N], n, m;
bool b[N];
void dfs(int x)
{
if (x == m)//更换输出条件
{
for (int i = 0; i < m; i++)//输出m个数
{
cout << a[i] << " ";
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++)
{
if (b[i] != true)
{
a[x] = i;
b[i] = true;
dfs(x + 1);
b[i] = false;
}
}
}
int main()
{
cin >> n >> m;
dfs(0);
return 0;
}
题目描述
从 1∼n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
输入格式
两个整数 n,m ,在同一行用空格隔开。输出格式
按照从小到大的顺序输出所有方案,每行 1 个。首先,同一行内的数升序排列,相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如 1 3 5 7 排在 1 3 6 8 前面)。
数据范围
n>0 ;0≤m≤n ;n+(n−m)≤25输入样例:
5 3
输出样例:
1 2 3 1 2 4 1 2 5 1 3 4 1 3 5 1 4 5 2 3 4 2 3 5 2 4 5 3 4 5
#include
#include
#include
using namespace std;
const int N = 1e6 + 10;
int n, m, a[N], b[N];
void dfs(int x, int st, int p)
{
if (x == p)
{
for (int i = 0; i < p; i++)
{
cout << a[i]<< " ";
}
cout << endl;
return;
}
for (int i = st; i <= n; i++)//不逆序就是字典序的核心
{
if (b[i] == 0)
{
a[x] = i;
b[i] = 1;
dfs(x + 1, i + 1, p);
b[i] = 0;
}
}
}
int main()
{
cin >> n >> m;
dfs(0, 1, m);
return 0;
}
题目描述
马在中国象棋以日字形规则移动。
请编写一段程序,给定 n×mn×m大小的棋盘,以及马的初始位置 (x,y)(x,y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点。
输入格式
第一行为整数 T(T<10)T(T<10),表示测试数据组数。
每一组测试数据包含一行,为四个整数,分别为棋盘的大小以及初始位置坐标 n,m,x,yn,m,x,y。(0≤x≤n−1,0≤y≤m−1,m<10,n<100≤x≤n−1,0≤y≤m−1,m<10,n<10)。输出格式
每组测试数据包含一行,为一个整数,表示马能遍历棋盘的途径总数,00 为无法遍历一次。
输入样例
输出样例1 5 4 0 0
32
#include
#include
#include
using namespace std;
const int N = 1e3 + 10;
int dx[8] = { -2,-2,-1,-1,1,1,2,2 };
int dy[8] = { -1,1,-2,2,-2,2,-1,1 };
int n, m, a, b, t, ans, d[N][N];
void dfs(int a, int b, int s)
{
if (s == n * m)//是否遍历完全部象棋
{
ans++;
return;
}
for (int i = 0; i < 8; i++)
{
int x = a + dx[i], y = b + dy[i];
if (x >= 0 && x < n && y >= 0 && y < m && d[x][y] == 0)
{
d[x][y] = 1;
dfs(x, y, s + 1);
d[x][y] = 0;
}
}
}
int main()
{
cin >> t;
while (t--)
{
memset(d, 0, sizeof(d));
ans = 0;
cin >> n >> m >> a >> b;
d[a][b] = 1;
dfs(a, b, 1);
cout << ans << endl;
}
return 0;
}
题目描述
牛牛打 CF,已知一场比赛有 n 道题,第 i 道题的满分为 ai,时间系数为 bi,保底分为 ci,本场比赛中每次错误提交罚 p 分。即如果牛牛在第 x 分钟,这道题 y 次错误提交后通过第 i 题,他将获得 max(ci,ai−x×bi−y×p) 分。比赛持续 t 分钟,即在 t 分钟(含第 t 分钟)内做出的题目计入总分。你已经知道了他第 i 题需要花费的时间 xi 和错误提交次数 yi ,请求出牛牛可能的最大得分。输入描述
第一行三个正整数 n,t,p。(n<9。t,p<1e9)
接下来 n 行,每行 5 个正整数 a,b,c,x,y。(a,b,c,x,y<1e9)输出描述
一个正整数,表示最大的分数
样例输入
3 120 50 500 2 150 6 1 1000 4 300 12 2 1500 6 450 120 3
样例输出
1266
说明
方案一:先开第 3 题,在 120 分钟时切掉,得到 1500−120×6−50×3=630。此时已无法继续切题,总分 630。 方案二:先开第 1 题,在 6 分钟时切掉,得到 438 分。再开第 2 题,在 18 分钟时切掉,得到 828 分。无法切第三题,总分 1266。 方案三:先开第 2 题,在 12 分钟时切掉,得到 852 分。再开第 1 题,在 18 分钟时切掉,得到 414 分。无法切第三题,总分 1266。 故可能的最大得分为 1266。
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL n, t, p, a[N], b[N], c[N], x[N], y[N], st[N];
LL res = 0, ma = 0;
void dfs(LL time)
{
if (time > t) return;
ma = max(ma, res);
for (int i = 1; i <= n; i++)
{
if (st[i] == 0)
{
st[i] = 1;
res += max(c[i], a[i] - b[i] * (time + x[i]) - y[i] * p);;
dfs(time + x[i]);
res -= max(c[i], a[i] - b[i] * (time + x[i]) - y[i] * p);;
st[i] = 0;
}
}
}
int main()
{
cin >> n >> t >> p;
for (int i = 1; i <= n; i++)
{
cin >> a[i] >> b[i] >> c[i] >> x[i] >> y[i];
}
dfs(0);
cout << ma << endl;
return 0;
}
题目描述
已知n个整数x1,x2,x3,...,xn,以及1个整数k( k < n)。从n个整数中任选k个整数相加,可分别得到一系列的和。列如当n = 4,k = 3,4个整数分别为3,7,12,19时,可得全部的组合与它们的和为:
3+7+12=22
3+7+19=29
7+12+19=38
3+12+19=34
现在,要求你计算出和为素数共有多少种
例如上例,只有一种的和为素数:3+7+19=29。输入
第一行两个空格隔开的整数n,k( 1 <= n <=20,k < n )。
第二行n个整数,分别为x1,x2,x3,...,xn( 1 <= xi <= 5 * 106)
输出
输出一个整数表示种类数。
样例输入4 3 3 7 12 19
样例输出
1
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL n, m, a[N], b[N], ans, res;
int cal(int x)//判断素数
{
if (x < 2) return 0;
for (int i = 2; i <= x / i; i++)
{
if (x % i == 0) return 0;
}
return 1;
}
void dfs(int x, int st, int p)
{
if (x == p + 1)
{
if (cal(res) == 1) ans++;
return;
}
for (int i = st; i <= n; i++)//避免重复
{
if (b[i] == 0)
{
res += a[i];
b[i] = 1;
dfs(x + 1, i + 1, p);
res -= a[i];
b[i] = 0;
}
}
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
//sort(a + 1, a + n + 1);//排不排序都行的
dfs(1, 1, m);
cout << ans << endl;
return 0;
}