一、DFS

(1)背包问题(每物一件)

  1. // 背包问题剪枝深搜
  2. #include <cstdio>
  3. const int maxn = 30;
  4. int n, V, maxValue = 0; //物品件数n,背包容量v,最大价值maxValue
  5. int w[maxn], c[maxn]; // w[i]为每件物品的重量,c[i]为每件物品的价值
  6. // DFS index为当前处理的编号
  7. // sumW和sumC分别为当前总重量和总价值
  8. void DFS (int index, int sumW, int sumC)
  9. {
  10. if(index == n){
  11. return;
  12. }
  13. DFS(index + 1, sumW, sumC);
  14. if(sumW + w[index] <= V)
  15. {
  16. if(sumC + c[index] >= maxValue)
  17. {
  18. maxValue = sumC + c[index]; // 更新最大价值maxValue
  19. }
  20. DFS(index + 1, sumW + w[index], sumC + c[index]);
  21. }
  22. }
  23. int main()
  24. {
  25. scanf("%d%d",&n,&V);
  26. for(int i = 0; i < n; ++i)
  27. {
  28. scanf("%d",&w[i]);
  29. }
  30. for(int i = 0; i < n; ++i)
  31. {
  32. scanf("%d",&c[i]);
  33. }
  34. DFS(0,0,0);
  35. printf("%d\n",maxValue);
  36. return 0;
  37. }
  38. //
  39. 输入:
  40. 5 8
  41. 3 5 1 2 2
  42. 4 5 2 1 3
  43. 输出:
  44. 10

这个是剪枝版本的,当然也可以像N皇后问题的全排列问题一样,把所有物品走一遍之后再看是否更新,但是这就意味着每个节点都考虑一遍,复杂度O(2**n**),上面代码用的深搜的剪枝算法。
当然目前的我建议用动态规划~~

(2)满足条件子序列问题

例如,从4个整数{2,3,3,4}中选择两个数,使它们的和为6,并输出平方和最大的方案,即{2,4},和之前的背包问题相比,背包问题没有要求选择物品的总数量。但是这个问题要求只能选择2个数,因此形参需要noeK来记录当前已经选择的个数。定义如下:

  1. void DFS (int index, int nowK, int sum, int sumSqu)
  1. // P273 条件下的子序列问题
  2. #include <cstdio>
  3. #include <vector>
  4. using namespace std;
  5. const int maxn = 100;
  6. // 从n个数中选k个数,使之和为x,最大平方和为maxSumSqu
  7. int n, k, x, maxSumSqu = -1, A[maxn];
  8. //temp存放临时方案,ans存放平方和最大的方案,不断更新
  9. vector<int> temp, ans;
  10. // 当前处理index号位的整数,当前已选整数个数为nowK
  11. // 当前已选整数之和为sum,当前已选整数平方和为sumSqu
  12. void DFS (int index, int nowK, int sum, int sumSqu)
  13. {
  14. if(nowK == k && sum == x)
  15. {
  16. if(sumSqu > maxSumSqu)
  17. {
  18. maxSumSqu = sumSqu;
  19. ans = temp;
  20. }
  21. return;
  22. }
  23. // 已经处理完n个数,或者超过k个数,或者和超过x,返回
  24. if (index == n || nowK > k || sum > x){
  25. return;
  26. }
  27. // 选index号位
  28. temp.push_back(A[index]);
  29. DFS(index + 1, nowK + 1, sum + A[index], sumSqu + A[index] * A[index]);
  30. temp.pop_back();
  31. // 不选index号位
  32. DFS(index + 1, nowK, sum , sumSqu);
  33. }
  34. int main()
  35. {
  36. // 从N个数中选k个数,使之和为x
  37. scanf("%d%d%d",&n,&k,&x);
  38. for(int i = 0; i < n; ++i)
  39. {
  40. scanf("%d",&A[i]);
  41. }
  42. DFS(0,0,0,0);
  43. printf("%d\n",maxSumSqu);
  44. return 0;
  45. }
  46. //
  47. 输入:
  48. 4 2 6
  49. 2 4 3 3
  50. //输出:
  51. 20

注意:不选index号位是下面的调用方式,必须index+1,想一下就懂,不+1就会死循环,出不去了,导致最后没有输出。

  1. // 不选index号位
  2. DFS(index + 1, nowK, sum, sumSqu);

修改题目:假设N个整数中的每一个都可以被选择多次,那么选择K个数,使得K个数之和恰好为X。
相比于刚才的问题,唯一的区别就是选择index号位后,不应直接进入index+1号位,而是继续index递归。即只需要把选index号位这条分支的代码改为:

  1. DFS(index, nowK + 1, sum + A[index], sumSqu + A[index] * A[index]);

(3)排列组合问题(8-1-B)

题目描述

排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你不用递归的方法输出所有组合。
例如n = 5 ,r = 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

输入

一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。

输出

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。

题解:

注意有两个return;把index的return去掉就会死循环。

代码:

  1. #include <cstdio>
  2. #include <vector>
  3. using namespace std;
  4. const int maxn = 100000;
  5. //从m个数中选n个数
  6. int m, n;
  7. vector<int> P;
  8. bool hashTable[maxn] = {false};
  9. //研究index号位,已选个数为nowK
  10. void generateP (int index, int nowK)
  11. {
  12. if(nowK == n)
  13. {
  14. for(int i = 0; i < n; ++i){
  15. printf("%d ",P[i]);
  16. }
  17. printf("\n");
  18. return;
  19. }
  20. if(index==m+1)
  21. return;
  22. P.push_back(index);
  23. generateP(index + 1, nowK + 1);
  24. P.pop_back();
  25. generateP(index + 1, nowK);
  26. }
  27. int main()
  28. {
  29. scanf("%d%d",&m, &n);
  30. generateP(1,0);
  31. return 0;
  32. }