当前位置:   article > 正文

全排列—dfs(递归算法&&手动模拟)_dfs全排列

dfs全排列

目录

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)来喽——找素数


1.dfs全排列深度优先算法思路导图

 此图来自AC中的Hasity作者,万分感谢;

2.dfs递归思想

  • dfs就是一条路走到头,当无法再往下走时就往上退一步,再看有没有路可以走,如果还没有路的话就再回退一步,重复这个步骤,直到找到可以走的道路;
  • 递归的主要思想在于不断调用本身的函数,层层深入,直到遇到递归终止条件后层层回溯,其思想与dfs基本吻合,从而调用递归实现dfs;
  • 正如y总讲到的回溯,它是在计算机底层执行的(系统有一个隐藏的栈帮我们做回溯),我们无法看到,也不需要操作。因此,理解并完成递归是它的一个难点;
  • 时间复杂度为 O(n*n!);空间复杂度为 O(n);

3.主旨展现

  • 用 a数组保存排列,当排列的长度为 n 时,是一种方案,进行输出;
  • 用bool数组b表示数字是否用过。当b[i]为1时,i已经被用过,b[i] 为0时,i 没有被用过;
  • dfs(i) 表示的含义是:在 a[i] 处填写数字,然后递归到下一个位置填写数字。
  • 回溯:第 i 个位置填写某个数字的所有情况都遍历后, 第 i 个位置填写下一个数字。

4.详解手动模拟

5.例题来喽

5.1例题(1)来喽——递归实现排列型枚举

题目描述

给定一个整数 m,将数字 1∼m 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式

共一行,包含一个整数 m。

输出格式

按字典序输出所有排列方案,每个方案占一行。

输入样例:

3

输出样例:

  1. 1 2 3
  2. 1 3 2
  3. 2 1 3
  4. 2 3 1
  5. 3 1 2
  6. 3 2 1

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 20;
  6. int a[N], m;
  7. bool b[N];
  8. void dfs(int x)
  9. {
  10. if (x == m)
  11. {
  12. for (int i = 0; i < m; i++)
  13. {
  14. cout << a[i] << " ";
  15. }
  16. cout << endl;
  17. return;//回溯
  18. }
  19. for (int i = 1; i <= m; i++)
  20. {
  21. if (b[i] != true)
  22. {
  23. a[x] = i;
  24. b[i] = true;
  25. dfs(x + 1);
  26. b[i] = false;
  27. }
  28. }
  29. return;//个人感觉加上更容易理解,目的是回溯
  30. }
  31. int main()
  32. {
  33. cin >> m;
  34. dfs(0);
  35. return 0;
  36. }

5.2例题(2)来喽——递归实现指数型枚举

题目描述

打印n个数中的m个数的全排列

样例输入

5 2

样例输出

  1. 1 2
  2. 1 3
  3. 1 4
  4. 1 5
  5. 2 1
  6. 2 3
  7. 2 4
  8. 2 5
  9. 3 1
  10. 3 2
  11. 3 4
  12. 3 5
  13. 4 1
  14. 4 2
  15. 4 3
  16. 4 5
  17. 5 1
  18. 5 2
  19. 5 3
  20. 5 4
  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 20;
  6. int a[N], n, m;
  7. bool b[N];
  8. void dfs(int x)
  9. {
  10. if (x == m)//更换输出条件
  11. {
  12. for (int i = 0; i < m; i++)//输出m个数
  13. {
  14. cout << a[i] << " ";
  15. }
  16. cout << endl;
  17. return;
  18. }
  19. for (int i = 1; i <= n; i++)
  20. {
  21. if (b[i] != true)
  22. {
  23. a[x] = i;
  24. b[i] = true;
  25. dfs(x + 1);
  26. b[i] = false;
  27. }
  28. }
  29. }
  30. int main()
  31. {
  32. cin >> n >> m;
  33. dfs(0);
  34. return 0;
  35. }

5.3例题(3)来喽——递归实现组合型枚举

题目描述

从 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. 1 2 3 
  2. 1 2 4 
  3. 1 2 5 
  4. 1 3 4 
  5. 1 3 5 
  6. 1 4 5 
  7. 2 3 4 
  8. 2 3 5 
  9. 2 4 5 
  10. 3 4 5 
  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 1e6 + 10;
  6. int n, m, a[N], b[N];
  7. void dfs(int x, int st, int p)
  8. {
  9. if (x == p)
  10. {
  11. for (int i = 0; i < p; i++)
  12. {
  13. cout << a[i]<< " ";
  14. }
  15. cout << endl;
  16. return;
  17. }
  18. for (int i = st; i <= n; i++)//不逆序就是字典序的核心
  19. {
  20. if (b[i] == 0)
  21. {
  22. a[x] = i;
  23. b[i] = 1;
  24. dfs(x + 1, i + 1, p);
  25. b[i] = 0;
  26. }
  27. }
  28. }
  29. int main()
  30. {
  31. cin >> n >> m;
  32. dfs(0, 1, m);
  33. return 0;
  34. }

5.4例题(4)来喽——马走日

题目描述

马在中国象棋以日字形规则移动。

请编写一段程序,给定 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. 1
  2. 5 4 0 0
 输出样例
32
  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 1e3 + 10;
  6. int dx[8] = { -2,-2,-1,-1,1,1,2,2 };
  7. int dy[8] = { -1,1,-2,2,-2,2,-1,1 };
  8. int n, m, a, b, t, ans, d[N][N];
  9. void dfs(int a, int b, int s)
  10. {
  11. if (s == n * m)//是否遍历完全部象棋
  12. {
  13. ans++;
  14. return;
  15. }
  16. for (int i = 0; i < 8; i++)
  17. {
  18. int x = a + dx[i], y = b + dy[i];
  19. if (x >= 0 && x < n && y >= 0 && y < m && d[x][y] == 0)
  20. {
  21. d[x][y] = 1;
  22. dfs(x, y, s + 1);
  23. d[x][y] = 0;
  24. }
  25. }
  26. }
  27. int main()
  28. {
  29. cin >> t;
  30. while (t--)
  31. {
  32. memset(d, 0, sizeof(d));
  33. ans = 0;
  34. cin >> n >> m >> a >> b;
  35. d[a][b] = 1;
  36. dfs(a, b, 1);
  37. cout << ans << endl;
  38. }
  39. return 0;
  40. }

5.5例题(5)来喽——开题顺序

题目描述
牛牛打 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)

输出描述

一个正整数,表示最大的分数

样例输入

  1. 3 120 50
  2. 500 2 150 6 1
  3. 1000 4 300 12 2
  4. 1500 6 450 120 3

样例输出

1266

说明

  1. 方案一:先开第 3 题,在 120 分钟时切掉,得到 1500120×650×3=630。此时已无法继续切题,总分 630
  2. 方案二:先开第 1 题,在 6 分钟时切掉,得到 438 分。再开第 2 题,在 18 分钟时切掉,得到 828 分。无法切第三题,总分 1266
  3. 方案三:先开第 2 题,在 12 分钟时切掉,得到 852 分。再开第 1 题,在 18 分钟时切掉,得到 414 分。无法切第三题,总分 1266
  4. 故可能的最大得分为 1266
  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. using namespace std;
  5. typedef long long LL;
  6. const int N = 1e6 + 10;
  7. LL n, t, p, a[N], b[N], c[N], x[N], y[N], st[N];
  8. LL res = 0, ma = 0;
  9. void dfs(LL time)
  10. {
  11. if (time > t) return;
  12. ma = max(ma, res);
  13. for (int i = 1; i <= n; i++)
  14. {
  15. if (st[i] == 0)
  16. {
  17. st[i] = 1;
  18. res += max(c[i], a[i] - b[i] * (time + x[i]) - y[i] * p);;
  19. dfs(time + x[i]);
  20. res -= max(c[i], a[i] - b[i] * (time + x[i]) - y[i] * p);;
  21. st[i] = 0;
  22. }
  23. }
  24. }
  25. int main()
  26. {
  27. cin >> n >> t >> p;
  28. for (int i = 1; i <= n; i++)
  29. {
  30. cin >> a[i] >> b[i] >> c[i] >> x[i] >> y[i];
  31. }
  32. dfs(0);
  33. cout << ma << endl;
  34. return 0;
  35. }

5.6例题(6)来喽——找素数

题目描述

已知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)


输出
输出一个整数表示种类数。


样例输入

  1. 4 3
  2. 3 7 12 19

样例输出

1
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. typedef long long LL;
  6. const int N = 1e6 + 10;
  7. LL n, m, a[N], b[N], ans, res;
  8. int cal(int x)//判断素数
  9. {
  10. if (x < 2) return 0;
  11. for (int i = 2; i <= x / i; i++)
  12. {
  13. if (x % i == 0) return 0;
  14. }
  15. return 1;
  16. }
  17. void dfs(int x, int st, int p)
  18. {
  19. if (x == p + 1)
  20. {
  21. if (cal(res) == 1) ans++;
  22. return;
  23. }
  24. for (int i = st; i <= n; i++)//避免重复
  25. {
  26. if (b[i] == 0)
  27. {
  28. res += a[i];
  29. b[i] = 1;
  30. dfs(x + 1, i + 1, p);
  31. res -= a[i];
  32. b[i] = 0;
  33. }
  34. }
  35. return;
  36. }
  37. int main()
  38. {
  39. cin >> n >> m;
  40. for (int i = 1; i <= n; i++) cin >> a[i];
  41. //sort(a + 1, a + n + 1);//排不排序都行的
  42. dfs(1, 1, m);
  43. cout << ans << endl;
  44. return 0;
  45. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/388229
推荐阅读
相关标签
  

闽ICP备14008679号