本次随笔专门讨论记忆化搜索
记忆化搜索常常和dp+递归结合,用来解决重复冗余问题。目的是加快搜索效率,避免tle。但是在数据范围较大的dp中使用会仍然有问题,比如爆栈(递归太深)
基本思路是通过一个记录状态的数组/记录答案的数组,保存前一阶段的状态/结果,从而在后一阶段的调用中直接获得答案,而不是重新调用递归。
我的代码:
其中注释部分是AC代码,而非注释部分是另外一种更为简便的记忆化方法
1 #include<iostream>
2 using namespace std;
3 typedef long long ll;
4 const int N=1000;
5 int w[25][25][25]={{{}}};
6 struct var{
7 ll a;
8 ll b;
9 ll c;
10 }g[N];
11 //记忆化搜索
12 int func(ll a,ll b,ll c){
13 if(a<=0||b<=0||c<=0){
14 return 1;
15 }
16 else if(a>20||b>20||c>20) return func(20,20,20);
17 else{
18 if(w[a][b][c]>0) return w[a][b][c];
19 else if(a<b&&b<c){
20 // w[a][b-1][c-1]=func(a,b-1,c-1);
21 // w[a][b][c-1]=func(a,b,c-1);
22 // w[a][b-1][c]=func(a,b-1,c);
23 w[a][b][c] = func(a,b-1,c-1)+func(a,b,c-1)-func(a,b-1,c);
24 // return w[a][b-1][c-1]+w[a][b][c-1]-w[a][b-1][c];
25 }
26 else{
27 // w[a-1][b-1][c-1]=func(a-1,b-1,c-1);
28 // w[a-1][b][c-1]=func(a-1,b,c-1);
29 // w[a-1][b-1][c]=func(a-1,b-1,c);
30 // w[a-1][b][c]=func(a-1,b,c);
31 w[a][b][c]=func(a-1,b,c-1)+func(a-1,b-1,c)+func(a-1,b,c)-func(a-1,b-1,c-1);
32 // 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];
33 }
34 return w[a][b][c];
35 }
36 }
37 int main(){
38 w[0][0][0]=1;
39 int i;
40 for(i=0;;i++){
41 cin >> g[i].a >> g[i].b >> g[i].c;
42 if(g[i].a==-1&&g[i].b==-1&&g[i].c==-1){
43 break;
44 }
45 }
46 for(int k=0;k<i;k++){
47 cout << "w(" << g[k].a << ", " << g[k].b << ", " << g[k].c << ") = " << func(g[k].a,g[k].b,g[k].c) << endl;
48 }
49 return 0;
50 }
其实注释部分(我自己的记忆化方法)的本质就是非注释部分。想想看,如果w[a][b][c]没有被计算过,那么肯定会在执行func(a,b,c)时给w[a][b][c]赋值,因此不必还单独给func(a,b-1,c-1)之类的赋值了。
另外对于记忆化搜索其实还有个值得注意的,就是在一个表达式中涉及多个状态数组值的问题,可以考虑先计算那个小的(也就是本来按顺序就排列在前面的)
不过好像也没有太大关系,主要自己觉得更顺心一点。