当前位置:   article > 正文

记忆化搜索_记忆化搜索时间复杂度

记忆化搜索时间复杂度

目录

 

A - Function Run Fun

B - 滑雪

C - 漫步校园

D - Free Candies

E - Zipper

F - Bone Collector


A - Function Run Fun

 POJ - 1579 

We all love recursion! Don't we?

Consider a three-parameter recursive function w(a, b, c):

if a <= 0 or b <= 0 or c <= 0, then w(a, b, c) returns:
1

if a > 20 or b > 20 or c > 20, then w(a, b, c) returns:
w(20, 20, 20)

if a < b and b < c, then w(a, b, c) returns:
w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c)

otherwise it returns:
w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1)

This is an easy function to implement. The problem is, if implemented directly, for moderate values of a, b and c (for example, a = 15, b = 15, c = 15), the program takes hours to run because of the massive recursion.

Input

The input for your program will be a series of integer triples, one per line, until the end-of-file flag of -1 -1 -1. Using the above technique, you are to calculate w(a, b, c) efficiently and print the result.

Output

Print the value for w(a,b,c) for each triple.

Sample Input

  1. 1 1 1
  2. 2 2 2
  3. 10 4 6
  4. 50 50 50
  5. -1 7 18
  6. -1 -1 -1

Sample Output

  1. w(1, 1, 1) = 2
  2. w(2, 2, 2) = 4
  3. w(10, 4, 6) = 523
  4. w(50, 50, 50) = 1048576
  5. w(-1, 7, 18) = 1

思路:按照体中给出的递归方法,时间复杂度应该在o(3^n)到o(4^n)之间,但如果将每个节点的值保存下来时间复杂度就可以降到n^3,也就是所谓的记忆化搜索。

题中写法:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<set>
  4. #include<vector>
  5. #define ll long long
  6. using namespace std;
  7. int w(int a, int b, int c)
  8. {
  9. if(a <=0 || b <= 0 || c <= 0) return 1;
  10. if(a > 20 || b > 20 || c > 20) return w(20,20,20);
  11. if(a < b && b < c) return w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c);
  12. return w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1);
  13. }
  14. int main()
  15. {
  16. int a,b,c;
  17. while(cin>>a>>b>>c)
  18. {
  19. if(a == -1 && b == -1 && c == -1) break;
  20. printf("w(%d, %d, %d) = %d\n",a,b,c,w(a,b,c));
  21. }
  22. return 0;
  23. }

记忆化搜索写法:

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<set>
  4. #include<vector>
  5. #define ll long long
  6. using namespace std;
  7. int dp[100][100][100];
  8. int w(int a, int b, int c)
  9. {
  10. if(a <=0 || b <= 0 || c <= 0) return 1;
  11. if(a > 20 || b > 20 || c > 20) return dp[a][b][c] = w(20,20,20);
  12. if(dp[a][b][c]) return dp[a][b][c];
  13. if(a < b && b < c) return dp[a][b][c] = w(a, b, c-1) + w(a, b-1, c-1) - w(a, b-1, c);
  14. return dp[a][b][c] = w(a-1, b, c) + w(a-1, b-1, c) + w(a-1, b, c-1) - w(a-1, b-1, c-1);
  15. }
  16. int main()
  17. {
  18. int a,b,c;
  19. while(cin>>a>>b>>c)
  20. {
  21. if(a == -1 && b == -1 && c == -1) break;
  22. printf("w(%d, %d, %d) = %d\n",a,b,c,w(a,b,c));
  23. }
  24. return 0;
  25. }

B - 滑雪

 POJ - 1088 

Glory非常喜欢玩滑滑梯游戏,下面给出了一个n,m的滑道,其中的数字表示滑道的高度。Glory可以从一个点出发向下滑行,每次只能滑行到相邻的位置(上下左右)中高度严格低于当前高度的地方,不能重复划行已经滑行过的地方,但他希望在这个滑道上滑行尽量远的距离,也即是找一条最长的滑道。

Input

第一行输入两个数n,m代表滑梯范围行n和列m(1 <= n,m <= 100)。下面是n行,每行有m个整数,代表高度h,(0<=h<=20000)

Output

输出一个值,代表Glory能够在滑滑梯上面滑行的最长长度是多少

Sample Input

3 3
9 1 2
5 6 7
8 4 3

Sample Output

4

Sample Input

4 7
7 6 5 4 3 2 1
1 5 1 1 1 1 1
1 4 3 1 1 1 1
1 5 6 7 8 1 1

Sample Output

7

hint

样例1:7->6->4->3 长度为4

  1. #include<iostream>
  2. using namespace std;
  3. const int N = 330;
  4. int dp[N][N];
  5. int r,c;
  6. int high[N][N];
  7. int mov[4][2] = {0,1,0,-1,1,0,-1,0};
  8. int dfs(int x, int y)
  9. {
  10. if(dp[x][y]) return dp[x][y];
  11. dp[x][y] = 1;
  12. for(int i=0; i<4; i++)
  13. {
  14. int nx = x + mov[i][0];
  15. int ny = y + mov[i][1];
  16. if(nx >=1 && nx <=r && ny >=1 && ny <=c && high[x][y] > high[nx][ny])
  17. {
  18. dp[x][y] = max(dp[x][y], 1+dfs(nx,ny));
  19. }
  20. }
  21. return dp[x][y];
  22. }
  23. int main()
  24. {
  25. cin>>r>>c;
  26. for(int i=1; i<=r; i++)
  27. for(int j=1; j<=c; j++)
  28. cin>>high[i][j];
  29. for(int i=1; i<=r; i++)
  30. for(int j=1; j<=c; j++)
  31. dp[i][j] = dfs(i,j);
  32. int ans = 0;
  33. for(int i=1; i<=r; i++)
  34. for(int j=1; j<=c; j++)
  35. ans = max(dp[i][j],ans);
  36. cout<<ans;
  37. return 0;
  38. }

C - 漫步校园

 HDU - 1428 

LL最近沉迷于AC不能自拔,每天寝室、机房两点一线。由于长时间坐在电脑边,缺乏运动。他决定充分利用每次从寝室到机房的时间,在校园里散散步。整个HDU校园呈方形布局,可划分为n*n个小方格,代表各个区域。例如LL居住的18号宿舍位于校园的西北角,即方格(1,1)代表的地方,而机房所在的第三实验楼处于东南端的(n,n)。因有多条路线可以选择,LL希望每次的散步路线都不一样。另外,他考虑从A区域到B区域仅当存在一条从B到机房的路线比任何一条从A到机房的路线更近(否则可能永远都到不了机房了…)。现在他想知道的是,所有满足要求的路线一共有多少条。你能告诉他吗?

Input

每组测试数据的第一行为n(2=<n<=50),接下来的n行每行有n个数,代表经过每个区域所花的时间t(0<t<=50)(由于寝室与机房均在三楼,故起点与终点也得费时)。

Output

针对每组测试数据,输出总的路线数(小于2^63)。

Sample Input

  1. 3
  2. 1 2 3
  3. 1 2 3
  4. 1 2 3
  5. 3
  6. 1 1 1
  7. 1 1 1
  8. 1 1 1

Sample Output

  1. 1
  2. 6

思路:这题翻译成人话就是求(1,1)到(n,n)的路径有多少条,对路径的要求是从A区域走到B区域时,B区域到终点的距离要小于A区域。首先求出每点到终点的最短路径距离,再求出符合题目要求的路径数量。

  1. #include<iostream>
  2. #include<queue>
  3. #include<cstring>
  4. #define ll long long
  5. using namespace std;
  6. const int N = 55;
  7. int t[N][N];
  8. ll dp[N][N];
  9. int dis[N][N];
  10. int n;
  11. struct node{
  12. int x;
  13. int y;
  14. };
  15. int mov[4][2] = {0,1,0,-1,1,0,-1,0};
  16. void bfs()
  17. {
  18. queue<node> q;
  19. q.push({n,n});
  20. dis[n][n] = t[n][n];//当前只知道终点到终点的距离。
  21. while(!q.empty())
  22. {
  23. node a = q.front();
  24. q.pop();
  25. int x = a.x;
  26. int y = a.y;
  27. for(int i=0; i<4; i++)
  28. {
  29. int nx = x + mov[i][0];
  30. int ny = y + mov[i][1];
  31. if(nx < 1 || nx > n || ny < 1 || ny > n) continue;
  32. if(dis[nx][ny] > dis[x][y] + t[nx][ny] || dis[nx][ny] == 0)
  33. {
  34. dis[nx][ny] = dis[x][y] + t[nx][ny];
  35. q.push({nx,ny});
  36. }
  37. }
  38. }
  39. }
  40. ll dfs(int x, int y)
  41. {
  42. if(dp[x][y]) return dp[x][y];
  43. for(int i=0; i<4; i++)
  44. {
  45. int nx = x + mov[i][0];
  46. int ny = y + mov[i][1];
  47. if(nx <1 || nx > n || ny < 1 || ny > n) continue;
  48. if(dis[x][y] > dis[nx][ny])
  49. {
  50. dp[x][y] += dfs(nx,ny);
  51. }
  52. }
  53. return dp[x][y];
  54. }
  55. int main()
  56. {
  57. while(cin>>n)
  58. {
  59. memset(dp, 0, sizeof dp);
  60. memset(dis, 0, sizeof dis);
  61. dp[n][n] = 1;
  62. for(int i=1; i<=n; i++)
  63. for(int j=1; j<=n; j++)
  64. cin>>t[i][j];
  65. bfs();
  66. cout<<dfs(1,1)<<endl;
  67. }
  68. return 0;
  69. }

D - Free Candies

 UVA - 10118 

题意:四堆糖果,每次从最上面拿一个,手中最多拿五个,如果手里的糖果有相同的,就可以把糖果占为己有,最多可以占有多少对糖果。

思路:遍历所有选法。

  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 55;
  6. int num[N][N];
  7. int vis[N];
  8. int n;
  9. int dp[N][N][N][N];
  10. int c[N];//这列拿到哪个糖果
  11. int dfs(int k)
  12. {
  13. int &rul = dp[c[1]][c[2]][c[3]][c[4]];
  14. if(rul != -1) return rul;
  15. rul = 0;
  16. if(k >= 5) return 0;
  17. for(int i=1; i<=4; i++)
  18. {
  19. if(c[i] == n) continue;
  20. c[i]++;
  21. if(vis[num[c[i]][i]])
  22. {
  23. vis[num[c[i]][i]] = 0;
  24. rul = max(rul, dfs(k-1)+1);
  25. vis[num[c[i]][i]] = 1;
  26. }
  27. else
  28. {
  29. vis[num[c[i]][i]] = 1;
  30. rul = max(rul, dfs(k+1));
  31. vis[num[c[i]][i]] = 0;
  32. }
  33. c[i]--;
  34. }
  35. return rul;
  36. }
  37. int main()
  38. {
  39. while(cin>>n && n)
  40. {
  41. for(int i=1; i<=n; i++)
  42. for(int j=1; j<=4; j++)
  43. cin>>num[i][j];
  44. memset(vis, 0, sizeof vis);
  45. memset(dp, -1, sizeof dp);
  46. memset(c, 0, sizeof c);
  47. cout<<dfs(0)<<endl;
  48. }
  49. return 0;
  50. }

E - Zipper

 HDU - 1501 

Given three strings, you are to determine whether the third string can be formed by combining the characters in the first two strings. The first two strings can be mixed arbitrarily, but each must stay in its original order.

For example, consider forming "tcraete" from "cat" and "tree":

String A: cat
String B: tree
String C: tcraete


As you can see, we can form the third string by alternating characters from the two strings. As a second example, consider forming "catrtee" from "cat" and "tree":

String A: cat
String B: tree
String C: catrtee


Finally, notice that it is impossible to form "cttaree" from "cat" and "tree".

Input

The first line of input contains a single positive integer from 1 through 1000. It represents the number of data sets to follow. The processing for each data set is identical. The data sets appear on the following lines, one data set per line.

For each data set, the line of input consists of three strings, separated by a single space. All strings are composed of upper and lower case letters only. The length of the third string is always the sum of the lengths of the first two strings. The first two strings will have lengths between 1 and 200 characters, inclusive.
 

Output

For each data set, print:

Data set n: yes

if the third string can be formed from the first two, or

Data set n: no

if it cannot. Of course n should be replaced by the data set number. See the sample output below for an example.

Sample Input

  1. 3
  2. cat tree tcraete
  3. cat tree catrtee
  4. cat tree cttaree

Sample Output

  1. Data set 1: yes
  2. Data set 2: yes
  3. Data set 3: no

思路:遍历所有情况的所有状态。

  1. #include<iostream>
  2. #include<cstring>
  3. using namespace std;
  4. const int N = 210;
  5. int dp[N][N];//当前情况下的匹配状态,0表示未匹配,1表示匹配成功,2表示匹配不成功
  6. char a[N],b[N],c[2*N];
  7. int dfs(int i,int j, int k)
  8. {
  9. if(dp[i][j]) return dp[i][j];
  10. if(k == strlen(c)) return dp[i][j] = 1;
  11. dp[i][j] = 2;
  12. if(a[i] == c[k]) dp[i][j] = dfs(i+1, j, k+1);
  13. if(b[j] == c[k] && dp[i][j] != 1) dp[i][j] = dfs(i, j+1, k+1);
  14. return dp[i][j];
  15. }
  16. int main()
  17. {
  18. int n;
  19. cin>>n;
  20. for(int i=1; i<=n; i++)
  21. {
  22. scanf("%s %s %s", a,b,c);
  23. memset(dp, 0, sizeof dp);
  24. if(dfs(0,0,0) == 1) printf("Data set %d: yes\n",i);
  25. else printf("Data set %d: no\n",i);
  26. }
  27. return 0;
  28. }

F - Bone Collector

 HDU - 2602 

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

Input

The first line contain a integer T , the number of cases.
Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

Output

One integer per line representing the maximum of the total value (this number will be less than 231).

Sample Input

  1. 1
  2. 5 10
  3. 1 2 3 4 5
  4. 5 4 3 2 1

Sample Output

14

思路:遍历所有情况。

  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 1010;
  6. struct node{
  7. int val;
  8. int wei;
  9. }bones[N];
  10. int dp[N][N];
  11. int n,v;
  12. int dfs(int x, int left)
  13. {
  14. if(x > n) return 0;
  15. if(dp[x][left]) return dp[x][left];
  16. if(bones[x].wei > left) return dp[x][left] = dfs(x+1, left);
  17. return dp[x][left] = max(dfs(x+1, left), dfs(x+1, left-bones[x].wei) + bones[x].val);
  18. }
  19. int main()
  20. {
  21. int t;
  22. cin>>t;
  23. while(t--)
  24. {
  25. cin>>n>>v;
  26. for(int i=1; i<=n; i++) cin>>bones[i].val;
  27. for(int i=1; i<=n; i++) cin>>bones[i].wei;
  28. cout<<dfs(1, v)<<endl;
  29. memset(dp, 0, sizeof dp);
  30. }
  31. return 0;
  32. }

G - FatMouse and Cheese

HDU - 1078 

有一种游戏是的玩法是这样的:
有一个n*n的格子,每个格子有一个数字。
遵循以下规则:
1. 玩家每次可以由所在格子向上下左右四个方向进行直线移动,每次移动的距离不得超过m
2. 玩家一开始在第一行第一列,并且已经获得该格子的分值
3. 玩家获得每一次移动到的格子的分值
4. 玩家下一次移动到达的格子的分值要比当前玩家所在的格子的分值要大。
5. 游戏所有数字加起来也不大,保证所有数字的和不会超过int型整数的范围
6. 玩家仅能在n*n的格子内移动,超出格子边界属于非法操作
7. 当玩家不能再次移动时,游戏结束
现在问你,玩家所能获得的最大得分是多少?

Input

有多组测试数据
每组测试样例第一行是两个整数n,m (1≤n≤100)(1≤m≤100),当n和m都是-1时为程序结束标志,直接退出即可
之后n行,每行n个数字,描述n*n的格子里的数字

Output

对于每组测试数据输出一行,这一行仅有一个整数,代表玩家所能获得的最高得分

Sample Input

  1. 3 1
  2. 1 2 5
  3. 10 11 6
  4. 12 12 7
  5. -1 -1

Sample Output

37
  1. #include<iostream>
  2. #include<cstring>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 110;
  6. int n,m;
  7. int mp[N][N];
  8. int dp[N][N];
  9. int mov[4][2] = {0,1,0,-1,1,0,-1,0};
  10. int dfs(int x, int y)
  11. {
  12. if(dp[x][y]) return dp[x][y];
  13. dp[x][y] = mp[x][y];
  14. for(int i=0; i<4; i++)
  15. for(int j=1; j<=m; j++)
  16. {
  17. int nx = x + mov[i][0] * j;
  18. int ny = y + mov[i][1] * j;
  19. if(nx >= 1 && nx <= n && ny >= 1 && ny <= n && mp[nx][ny] > mp[x][y])
  20. dp[x][y] = max(mp[x][y] + dfs(nx, ny), dp[x][y]);
  21. }
  22. return dp[x][y];
  23. }
  24. int main()
  25. {
  26. while(cin>>n>>m)
  27. {
  28. if(n == -1 && m == -1) break;
  29. for(int i=1; i<=n; i++)
  30. for(int j=1; j<=n; j++)
  31. cin>>mp[i][j];
  32. cout<<dfs(1,1)<<endl;
  33. memset(dp, 0, sizeof dp);
  34. }
  35. return 0;
  36. }

 

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

闽ICP备14008679号