1 预备基础

1.1 递归函数 Recursion

Fibonacci的递归实现以指数级别展开,由于调用过程中大量出现重复计算,考虑通过记忆化搜索/动态规划进行优化。

1.2 栈 Stack : Last In Fiset Out

push :在Stack的顶端放入一组数据
pop:从顶端取出一组数据

(C++)
stack::pop 移除最顶端数据
stack::top 访问最顶端的数据(peek)

  1. #include <stack>
  2. #include <iostream>
  3. int main(){
  4. std::stack<int> s;
  5. s.push(1);//push 1 to the top element
  6. s.push(321);
  7. std::cout<<s.top();//print 321
  8. s.pop();// remove the top element
  9. return 0;
  10. }

1.3 队列 Queue : First In First Out

push :在Queue的队尾放入一组数据
pop:从顶端取出一组数据

(C++)
queue::push
queue::pop
queue::front

  1. #include <queue>
  2. #include <iostream>
  3. int main(){
  4. std::queue<int> s;
  5. s.push(1);
  6. s.push(321);//push 1 to the last element
  7. //{1,321}
  8. std::cout<<s.front();//print 1
  9. s.pop();// remove the top element
  10. //{321}
  11. return 0;
  12. }

2 算法具体

2.1 Depth-First Search (DFS)

基于栈,从某个状态开始,不断转移状态直到无法继续转移,然后回退到前一状态继续转移到其它状态。

  1. //(Template)Problem:部分和问题
  2. //给定整数a1, a2, a3, ..., an ;判断是否可以从中选出若干数,使它们之和为k
  3. #include <iostream>
  4. int a[100];
  5. int n, k;
  6. bool dfs(int i, int sum);
  7. int main()
  8. {
  9. std::cin >> n; //input the length of array
  10. for (int i = 1; i <= n; i++)
  11. std::cin >> a[i];
  12. std::cin >> k; //input goals
  13. if(dfs(0,0))
  14. std::cout<<"Yes";
  15. else
  16. std::cout<<"No";
  17. return 0;
  18. }
  19. //已经得到前i项和sum,处理i项之后的分支
  20. bool dfs(int i, int sum)
  21. {
  22. if(i==n) //截止条件:如果在这一层就到底了
  23. return sum==k;//若前n项都计算完成(再没法计算,已为最大),返回sum是否等于目标值k
  24. if(dfs(i+1,sum)) //状态A:不将第i项包括在内
  25. return true;
  26. if(dfs(i+1,sum+a[i+1]))//状态B:将第i项包括在内
  27. return true;
  28. return false; //返回条件or判否条件
  29. }
  1. // Problem: Lake Counting (POJ2386)
  2. // Description:
  3. // 由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。
  4. // 我们用一个NxM(1<=N<=100;1<=M<=100)网格图表示。每个网格中有水('W') 或是旱地('.')。
  5. // 一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。
  6. // 给出约翰田地的示意图,确定当中有多少水坑。
  7. // Sample Input:
  8. // 10 12
  9. // W........WW.
  10. // .WWW.....WWW
  11. // ....WW...WW.
  12. // .........WW.
  13. // .........W..
  14. // ..W......W..
  15. // .W.W.....WW.
  16. // W.W.W.....W.
  17. // .W.W......W.
  18. // ..W.......W.
  19. #include <iostream>
  20. int field[101][101] = {{0}};
  21. int move[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
  22. int N, M;
  23. int nx, ny;
  24. void dfs(int x, int y);
  25. int main()
  26. {
  27. //input
  28. char tmp;
  29. std::cin >> N >> M;
  30. for (int i = 1; i <= N; i++)
  31. for (int j = 1; j <= M; j++)
  32. {
  33. std::cin >> tmp;
  34. if (tmp == 'W')
  35. field[i][j]++;
  36. }
  37. int res = 0;
  38. for (int i = 1; i <= N; i++)
  39. for (int j = 1; j <= M; j++)
  40. if (field[i][j])
  41. {
  42. dfs(i, j);
  43. res++;
  44. }
  45. std::cout << res;
  46. return 0;
  47. }
  48. void dfs(int x, int y)
  49. {
  50. //此时有field[x][y]==1
  51. field[x][y]--;//归0
  52. for (int i = 0; i < 8; i++)
  53. {
  54. nx = x + move[i][0];
  55. ny = y + move[i][1];
  56. if (nx > 0 && nx <= N && ny > 0 && ny <= M)
  57. if (field[nx][ny])
  58. dfs(nx, ny);
  59. }
  60. return;
  61. }

2.2 Breadth-First Search (BFS)

利用队列,从某个状态出发,搜索距离初始状态近的状态。复杂度O(状态数*转移方式)
首先将初始状态加入到队列,从队列最前端不断取出状态,把从该状态可以转移到的状态中尚未访问过的部分加入队列,直到队列被取空or找到了问题的解。常用于求最短路径、最少操作等问题。
状态简单时,可以构造成pair或编码成int来表达状态。复杂时考虑封装类来表示状态。
通过标记来管理访问过的状态,做到由近及远的搜索。如对于求解最短距离的问题,不妨用d[N][M]把最短距离保存起来,同时用足够大常数初始化。

  1. // (template)Problem: 迷宫最短路径
  2. // 给定N*M迷宫,迷宫由通道和墙壁组成,每一步可以向临接的上下左右四格的通道移动。求出从起点到终点所需的最小步数。
  3. // 假设从起点一定可以移动到终点。N,M<=100.
  4. #include <iostream>
  5. #include <queue>
  6. const int INF = 1000000000;
  7. typedef std::pair<int, int> P;
  8. int maze[101][101] = {{0}}; // map:0-wall(default);1-way
  9. int dis[101][101] = {{INF}}; // distance
  10. int N, M; // edge of the map
  11. int s[2], g[2]; // start pos& goal pos
  12. int move[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; // change the status
  13. int bfs()
  14. {
  15. std::queue<P> que;
  16. // initialization
  17. que.push(P(s[0], s[1]));
  18. dis[s[0]][s[1]] = 0;
  19. while (que.size())
  20. {
  21. //取出que最前端元素
  22. P p = que.front();
  23. que.pop();
  24. // arrived at the goal pos -> jump out from the scope
  25. if (p.first == g[0] && p.second == g[1])
  26. break;
  27. for (int i = 0; i < 4; i++)
  28. {
  29. // current pos:
  30. int currentPos[2] = {p.first + move[i][1], p.second + move[i][2]};
  31. // not beyond the edge:
  32. if (currentPos[0] > 0 && currentPos[1] > 0 && currentPos[0] <= N && currentPos[1] <= M)
  33. // is way && have not accessing
  34. if (maze[currentPos[0]][currentPos[1]] && dis[currentPos[0]][currentPos[1]])
  35. {
  36. que.push(P(currentPos[0], currentPos[1]));
  37. dis[currentPos[0]][currentPos[1]] = dis[p.first][p.second] + 1;
  38. }
  39. }
  40. }
  41. return dis[g[0]][g[1]];
  42. }
  43. int main()
  44. {
  45. // input
  46. char tmp;
  47. std::cin >> N >> M;
  48. for (int i = 1; i <= N; i++)
  49. for (int j = 1; j <= M; j++)
  50. {
  51. std::cin >> tmp;
  52. if (tmp == '#')
  53. continue;
  54. if (tmp == 'S')
  55. {
  56. s[1] = i;
  57. s[2] = j;
  58. }
  59. else if (tmp == 'G')
  60. {
  61. g[1] = i;
  62. g[2] = j;
  63. }
  64. maze[i][j]++;
  65. }
  66. // solve
  67. std::cout << bfs();
  68. }

拓展:Iterative Deepening Depth-First Search (IDDFS)迭代加深深度优先搜索

2.3 特殊状态枚举

生成可行解空间多数采用深度优先搜索,但在状态空间相对特殊时可以简短实现。
C++标准库提供函数nextperutation,可以将基础穷竭搜索:DFS&BFS - 图1个元素共基础穷竭搜索:DFS&BFS - 图2种不同的排列生成出来。或使用位运算枚举从基础穷竭搜索:DFS&BFS - 图3个元素中取出基础穷竭搜索:DFS&BFS - 图4个的共![](https://cdn.nlark.com/yuque/__latex/4be6d69d37a3e8ff095f1031c9ee6e77.svg#card=math&code=C%5En%7Bk%7D&height=20&width=21)种状态或某集合中的全部子集。