[关闭]
@rg070836rg 2018-04-03T20:47:58.000000Z 字数 7784 阅读 1072

搜索专题

算法


  1. 搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。
  2. 如迷宫问题:进入迷宫后,先随意选择一个前进方向,一步步向前试探前进,如果碰到死胡同,说明前进方向已无路可走,这时,首先看其它方向是否还有路可走,如果有路可走,则沿该方向再向前试探;如果已无路可走,则返回一步,再看其它方向是否还有路可走;如果有路可走,则沿该方向再向前试探。按此原则不断搜索回溯再搜索,直到找到新的出路或从原路返回入口处无解为止。
  3. 递归回溯法算法框架[一]
  4. int Search(int k)
  5.  {
  6.  for (i=1;i<=算符种数;i++)
  7.   if (满足条件)
  8.    {
  9.     保存结果
  10.     if (到目的地) 输出解;
  11.     else Search(k+1);
  12.     恢复:保存结果之前的状态{回溯一步}
  13.     }
  14.  }
  15. 递归回溯法算法框架[二]
  16. int Search(int k)
  17.  {
  18.   if (到目的地) 输出解;
  19.    else
  20.     for (i=1;i<=算符种数;i++)
  21.      if (满足条件)
  22.        {
  23.         保存结果;
  24.     Search(k+1);
  25.         恢复:保存结果之前的状态{回溯一步}
  26.        }
  27.  }

问题:

1 全排列

编写一个输出1,2,3...n,n个数字所组成的所有排列

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. using namespace std;
  5. #define N 3
  6. //回溯写全排列
  7. bool b[N+1]={0};
  8. int a[N+1]={0};
  9. void print_elems()
  10. {
  11. for (int i=1;i<=N;i++)
  12. {
  13. cout<<a[i]<<" ";
  14. }
  15. cout<<endl;
  16. }
  17. void dfs(int t)
  18. {
  19. if (t==N+1)
  20. {
  21. //到达目的地,输出解
  22. print_elems();
  23. return ;
  24. }
  25. for(int i=1;i<=N;i++)
  26. {
  27. if(b[i]==false)//条件即数字还没被用过
  28. {
  29. b[i]=true;
  30. a[t]=i;//
  31. dfs(t+1);
  32. b[i]=false;
  33. }
  34. }
  35. }
  36. void dfs2(int t)
  37. {
  38. for(int i=1;i<=N;i++)
  39. {
  40. if(b[i]==false)//条件即数字还没被用过
  41. {
  42. b[i]=true;
  43. a[t]=i;//
  44. if(t<N)
  45. dfs2(t+1);
  46. else
  47. print_elems();
  48. b[i]=false;
  49. }
  50. }
  51. }
  52. int main()
  53. {
  54. //dfs(1);
  55. dfs2(1);
  56. return 0;//结束
  57. }

2 字母全排列

输出字母a、b、c、d,4个元素全排列的每一种排列。

  1. #include<cstdio>
  2. #include<iostream>
  3. #include<algorithm>
  4. using namespace std;
  5. #define N 3
  6. //回溯写全排列
  7. bool b[N+1]={0};
  8. char a[N+1];
  9. char c[N+1];
  10. void print_elems()
  11. {
  12. for (int i=1;i<=N;i++)
  13. {
  14. cout<<a[i]<<" ";
  15. }
  16. cout<<endl;
  17. }
  18. void dfs(int t)
  19. {
  20. if (t==N+1)
  21. {
  22. //到达目的地,输出解
  23. print_elems();
  24. return ;
  25. }
  26. for(int i=1;i<=N;i++)
  27. {
  28. if(b[i]==false)//条件即数字还没被用过
  29. {
  30. b[i]=true;
  31. a[t]=c[i];//
  32. dfs(t+1);
  33. b[i]=false;
  34. }
  35. }
  36. }
  37. int main()
  38. {
  39. for(int i=1;i<=N;i++)
  40. c[i]='a'+i-1;
  41. dfs(1);
  42. return 0;//结束
  43. }

3 素数环

输入
有多组测试数据,每组输入一个n(0 输出
每组第一行输出对应的Case序号,从1开始。
如果存在满足题意叙述的素数环,从小到大输出。
否则输出No Answer。
样例输入
6
8
3
0
样例输出
Case 1:
1 4 3 2 5 6
1 6 5 2 3 4
Case 2:
1 2 3 8 5 6 7 4
1 2 5 8 3 4 7 6
1 4 7 6 5 8 3 2
1 6 7 4 3 8 5 2
Case 3:
No Answer

  1. /*
  2. 素数环:给定n,1~n组成一个素数环,相邻两个数的和为素数。
  3. 首先偶数(2例外,但是本题不会出现两个数的和为2)不是素数,
  4. 所以素数环里奇偶间隔。如果n是奇数,必定有两个奇数相邻的情况。
  5. 所以当n为奇数时,输出“No Answer”。
  6. 当n == 1时只1个数,算作自环,输出1
  7. 所有n为偶数的情况都能变成奇偶间隔的环-----所以都有结果。
  8. */
  9. #include<iostream>
  10. #include<cstring>
  11. using namespace std;
  12. int prime[40]={1,1,0,0,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,0,1,1,01,1,1,0,1,1};
  13. int b[21]={0};
  14. int a[21];
  15. int n;
  16. bool Is_prime(int x)
  17. {
  18. for (int i = 2; i < x; i++)//也可用n/2,不过计算量要比sqrt大一些
  19. {
  20. if (n%i == 0)
  21. return false;
  22. }
  23. return true;
  24. }
  25. void DFS(int k)
  26. {
  27. //if(k==n+1 && prime[a[n]+a[1]]==0)
  28. if(k==n+1 && Is_prime(a[n]+a[1])==0)
  29. {
  30. for(int i=1;i<=n;i++)
  31. cout<<a[i];
  32. cout<<endl;
  33. return;
  34. }
  35. for(int i=2;i<=n;++i)
  36. {
  37. if(!b[i]&&!prime[i+a[k-1]])
  38. {
  39. b[i]=1;
  40. a[k]=i;
  41. DFS(k+1);
  42. b[i]=0;
  43. }
  44. }
  45. }
  46. int main()
  47. {
  48. cin>>n;
  49. b[1]=a[1]=1;
  50. DFS(2);
  51. return 0;
  52. }

4 r排列

设有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r

  1. #include<iostream>
  2. using namespace std;
  3. int n,r;
  4. int a[1001]={0};
  5. int b[1001]={0};
  6. int cnt=0;
  7. void pr()
  8. {
  9. for(int i=1;i<=r;i++)
  10. {
  11. cout<<a[i]<<" ";
  12. }
  13. cout<<endl;
  14. }
  15. void dfs(int t)
  16. {
  17. if(t==r+1)
  18. {
  19. pr();
  20. cnt++;
  21. return ;
  22. }
  23. for(int i=1;i<=n;i++)
  24. {
  25. if(b[i]==0)
  26. {
  27. b[i]=1;
  28. a[t]=i;
  29. dfs(t+1);
  30. b[i]=0;
  31. }
  32. }
  33. }
  34. int main()
  35. {
  36. cin>>n>>r;
  37. dfs(1);
  38. cout<<cnt;
  39. return 0;
  40. }

5 分拆数

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14

  1. // ConsoleApplication1.cpp: 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include<iostream>
  5. using namespace std;
  6. int n;
  7. int a[1001] = { 1 };
  8. int res = 0;
  9. void pr(int x)
  10. {
  11. res++;
  12. for (int i = 1; i <= x; i++)
  13. cout << a[i] << " ";
  14. cout << endl;
  15. }
  16. void dfs(int n, int q)
  17. {
  18. if (n == 0)
  19. {
  20. if (q - 1>1)
  21. pr(q - 1);
  22. return;
  23. }
  24. else
  25. {
  26. for (int i = a[q - 1]; i <= n; i++)
  27. {
  28. if (n >= i)
  29. {
  30. a[q] = i;
  31. n -= i;
  32. dfs(n, q + 1);
  33. n += i;
  34. }
  35. }
  36. }
  37. }
  38. void dfs2(int p, int q)
  39. {
  40. int i;
  41. for (i = a[q - 1]; i <= p; i++)
  42. {
  43. if (i<n)
  44. {
  45. a[q] = i;
  46. p -= i;
  47. if (p == 0)pr(q);
  48. else dfs2(p, q + 1);
  49. p += i;
  50. }
  51. }
  52. }
  53. int main()
  54. {
  55. cin >> n;
  56. dfs2(n, 1);
  57. cout << res;
  58. return 0;
  59. }

6 八皇后

问题:要在国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)

  1. #include<iostream>
  2. #include<cmath>
  3. using namespace std;
  4. int n;
  5. int a[1001]={0};//a[1]=2 表示第一行皇后在第二列
  6. int b[1001]={0};
  7. int c[1001]={0};
  8. int d[1001]={0};
  9. int res=0;
  10. void dfs(int t)
  11. {
  12. if(t==n+1)
  13. {
  14. res++;
  15. return ;
  16. //输出结果
  17. }
  18. for(int i=1;i<=n;i++)
  19. {
  20. if(b[i]==0 && c[t+i]==0 && d[t-i+n-1]==0)
  21. {
  22. a[t]=i;
  23. b[i]=1;//占领列
  24. c[t+i]=1;
  25. d[t-i+n-1]=1;
  26. dfs(t+1);
  27. b[i]=0;
  28. c[t+i]=0;
  29. d[t-i+n-1]=0;
  30. }
  31. }
  32. }
  33. int main()
  34. {
  35. cin>>n;
  36. dfs(1);
  37. cout<<res;
  38. return 0;
  39. }
  1. #include<iostream>
  2. #include<cmath>
  3. using namespace std;
  4. int n;
  5. int a[1001]={0};//a[1]=2 表示第一行皇后在第二列
  6. int b[1001]={0};
  7. int c[1001]={0};
  8. int d[1001]={0};
  9. int res=0;
  10. void dfs(int t)
  11. {
  12. for(int i=1;i<=n;i++)
  13. {
  14. if(b[i]==0 && c[t+i]==0 && d[t-i+n-1]==0)
  15. {
  16. a[t]=i;
  17. b[i]=1;//占领列
  18. c[t+i]=1;
  19. d[t-i+n-1]=1;
  20. if(t==n)
  21. {
  22. res++;
  23. //输出结果
  24. }
  25. else dfs(t+1);
  26. b[i]=0;
  27. c[t+i]=0;
  28. d[t-i+n-1]=0;
  29. }
  30. }
  31. }
  32. int main()
  33. {
  34. cin>>n;
  35. dfs(1);
  36. cout<<res;
  37. return 0;
  38. }

7 马的遍历

  1. #include<iostream>
  2. #include<cmath>
  3. using namespace std;
  4. int a[1001][1] = { 0 };//用来存储每一步对应的xy
  5. int res = 0;
  6. int x[5] = { 0,2,1,-1,-2 };
  7. int y[5] = { 0,1,2,2,1 };
  8. //(0,0)-->(8,4)
  9. void dfs(int t)
  10. {
  11. for (int i = 1; i <= 4; i++)
  12. {
  13. if (a[t - 1][0] + x[i] >= 0 && a[t - 1][0] + x[i] <= 4 &&
  14. a[t - 1][2] + y[i] >= 0 && a[t - 1][3] + y[i] <= 8)//如果不出格子
  15. {
  16. a[t][0] = a[t - 1][0] + x[i];
  17. a[t][4] = a[t - 1][5] + y[i];
  18. if (a[t][0] == 4 && a[t][6] == 8)
  19. {
  20. res++;
  21. return;
  22. }
  23. dfs(t + 1);
  24. //a[t][0] = 0;
  25. //a[t][7] = 0;
  26. }
  27. }
  28. }
  29. void dfs2(int t)
  30. {
  31. if (a[t-1][0] == 4 && a[t-1][8] == 8)
  32. {
  33. res++;
  34. return;
  35. }
  36. for (int i = 1; i <= 4; i++)
  37. {
  38. if (a[t - 1][0] + x[i] >= 0 && a[t - 1][0] + x[i] <= 4 &&
  39. a[t - 1][9] + y[i] >= 0 && a[t - 1][10] + y[i] <= 8)//如果不出格子
  40. {
  41. a[t][0] = a[t - 1][0] + x[i];
  42. a[t][11] = a[t - 1][12] + y[i];
  43. dfs(t + 1);
  44. //a[t][0] = 0;
  45. //a[t][13] = 0;
  46. }
  47. }
  48. }
  49. int main()
  50. {
  51. dfs(1);
  52. cout << res;
  53. return 0;
  54. }

8 工作安排

设有A,B,C,D,E五人从事J1,J2,J3,J4,J5五项工作,每人只能从事一项,他们的效益如下。

image_1c9lv6vuo1bb41oipkdd3hd1opk9.png-24.8kB

  1. #include<iostream>
  2. using namespace std;
  3. int a[6]={0};
  4. int b[6]={0};
  5. int c[6]={0};
  6. int job[6][15] ={
  7. {0,0,0,0,0,0},
  8. {0,13,11,10,4,7},
  9. {0,13,10,10,8,5},
  10. {0,5,9,7,7,4},
  11. {0,15,12,10,11,5},
  12. {0,10,11,8,8,4}
  13. };
  14. //job[1][16]
  15. int v=0;
  16. int maxv=-1;
  17. void dfs(int t)
  18. {
  19. if(t==6)
  20. {
  21. if(maxv<v)
  22. {
  23. maxv=v;
  24. for(int i=1;i<=5;i++)
  25. c[i]=a[i];
  26. }
  27. //比较价值最大
  28. return ;
  29. }
  30. for(int i=1;i<=5;i++)
  31. {
  32. if(b[i]==0)
  33. {
  34. //保存结果
  35. a[t]=i;
  36. b[i]=1;
  37. v+=job[t][i];
  38. dfs(t+1);
  39. //a[t]=0;
  40. b[i]=0;
  41. v-=job[t][i];
  42. }
  43. }
  44. }
  45. void dfs2(int t)
  46. {
  47. for(int i=1;i<=5;i++)
  48. {
  49. if(b[i]==0)
  50. {
  51. //保存结果
  52. a[t]=i;
  53. b[i]=1;
  54. v+=job[t][i];
  55. if(t==5)
  56. {
  57. if(maxv<v)
  58. {
  59. maxv=v;
  60. for(int i=1;i<=5;i++)
  61. c[i]=a[i];
  62. }
  63. //比较价值最大
  64. }
  65. else
  66. {
  67. dfs(t+1);
  68. }
  69. //a[t]=0;
  70. b[i]=0;
  71. v-=job[t][i];
  72. }
  73. }
  74. }
  75. int main()
  76. {
  77. //cout<<job[1][17];
  78. dfs(1);
  79. for(int i=1;i<=5;i++)
  80. cout<<c[i] <<" ";
  81. cout<<endl<<maxv<<endl;
  82. return 0;
  83. }

9 选书问题

学校放寒假时,信息学竞赛辅导老师有A,B,C,D,E五本书,要分给参加培训的张、王、刘、孙、李五位同学,每人只能选一本书。老师事先让每个人将自己喜欢的书填写在如下的表格中。然后根据他们填写的表来分配书本,希望设计一个程序帮助老师求出所有可能的分配方案,使每个学生都满意。

image_1c9m74k2n19i412kc1cm01bj81pu19.png-34.9kB

  1. #include<iostream>
  2. using namespace std;
  3. int like[6][19]={
  4. {0,0,0,0,0,0},
  5. {0,0,0,1,1,1},
  6. {0,1,0,1,1,1},
  7. {0,1,1,1,1,1},
  8. {0,1,1,0,0,1},
  9. {0,1,1,1,0,0},
  10. };
  11. int a[6] ={0};
  12. int b[6] ={0};
  13. int res=0;
  14. void dfs(int t)
  15. {
  16. for(int i=1;i<=5;i++)
  17. {
  18. if(b[i]==0 && like[t][i]==1)
  19. {
  20. //a[t]=i;
  21. b[i]=1;
  22. if(t==5)
  23. {
  24. res++;
  25. }
  26. else
  27. {
  28. dfs(t+1);
  29. }
  30. //a[t]=0;
  31. b[i]=0;
  32. }
  33. }
  34. }
  35. int main()
  36. {
  37. dfs(1);
  38. cout<<res;
  39. return 0;
  40. }

10 马踏棋盘

1.png-430.5kB

  1. #include<iostream>
  2. using namespace std;
  3. int a[6][6] ={0};
  4. int b[6][6] ={0};
  5. int x[9]={0,1,2,2,1,-1,-2,-2,-1};
  6. int y[9]={0,-2,-1,1,2,2,1,-1,-2};
  7. int c[26][2] = {0}; //c[t][0]:x c[t][1]:y
  8. int res=0;
  9. void dfs(int t)
  10. {
  11. for(int i=1;i<=8;i++)
  12. {
  13. int X=c[t-1][0]+x[i];
  14. int Y=c[t-1][1]+y[i];
  15. if( X>=1 && X<=5
  16. && Y>=1 && Y<=5
  17. && b[X][Y]==0)
  18. {
  19. b[X][Y]=1;//保存状态
  20. a[X][Y]=t;//记录值
  21. c[t][0]=X;
  22. c[t][1]=Y;
  23. //搜索下一层
  24. if(t==25){
  25. res++;
  26. }
  27. else
  28. {
  29. dfs(t+1);
  30. }
  31. //恢复状态
  32. b[X][Y]=0;
  33. a[X][Y]=0;
  34. c[t][0]=0;
  35. c[t][1]=0;
  36. }
  37. }
  38. }
  39. int main()
  40. {
  41. b[1][1]=1;//初始条件!!
  42. c[1][0]=1;c[1][1]=1;
  43. dfs(2);
  44. cout<<res;
  45. return 0;
  46. }

11 李白打酒

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。 

请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。

注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。
  1. #include<iostream>
  2. #include<cmath>
  3. using namespace std;
  4. int res;
  5. void dfs(int dian,int hua,int jiu)
  6. {
  7. if(dian>0)
  8. {
  9. dfs(dian-1,hua,jiu*2);
  10. }
  11. if(hua>1)
  12. {
  13. dfs(dian,hua-1,jiu-1);
  14. }
  15. //结束条件
  16. if(dian==0 && hua==1 && jiu==1)
  17. res++;
  18. }
  19. int main()
  20. {
  21. dfs(5,10,2);
  22. cout<<res;
  23. return 0;
  24. }

12 凑数字

看这个算式:
☆☆☆ + ☆☆☆ = ☆☆☆
如果每个五角星代表 1 ~ 9 的不同的数字。
这个算式有多少种可能的正确填写方法?
173 + 286 = 459
295 + 173 = 468
173 + 295 = 468
183 + 492 = 675
以上都是正确的填写法!
注意:
111 + 222 = 333 是错误的填写法!
因为每个数字必须是不同的!
也就是说:1~9中的所有数字,每个必须出现且仅出现一次!
注意:
不包括数字“0”!
注意:
满足加法交换率的式子算两种不同的答案。
所以答案肯定是个偶数!

注意:
只要求计算不同的填法的数目
不要求列出所有填写法
更不要求填写源代码!

  1. //思路 全排列,加判断等式
  2. #include<iostream>
  3. using namespace std;
  4. int res[10000];
  5. int b[10000]={0};
  6. int n;
  7. int resnum=0;
  8. void dfs(int t)
  9. {
  10. for(int i=1;i<=n;i++)
  11. {
  12. if(b[i]==0) //剪枝
  13. {
  14. b[i]=1;
  15. res[t]=i;
  16. if(t==n)
  17. {
  18. //123456789
  19. int num1 = res[1]*100+res[2]*10+res[3];
  20. int num2 = res[4]*100+res[5]*10+res[6];
  21. int num3 = res[7]*100+res[8]*10+res[9];
  22. if(num1+num2==num3)
  23. {
  24. cout<<num1<<"+"<<num2<<"="<<num3<<endl;
  25. resnum++;
  26. }
  27. }
  28. else
  29. dfs(t+1);
  30. b[i]=0;
  31. }
  32. }
  33. }
  34. int main(){
  35. n=9;
  36. dfs(1);
  37. cout<<resnum;
  38. return 0;
  39. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注