今天的時間較短,沒有刷很多的題,只刷了一道記憶化搜索的題目,還調試了半天(就是因為輸出沒有換行TwT)。但就是這道題讓我把新手村A掉啦~\(≧▽≦)/~
記憶化搜索
·記憶化搜索是啥?
所謂記憶化搜索,就是讓程序實現自動記憶已經搜索過的東西,這樣如果再次搜到這個東西,就可以直接調用了。
·記憶化搜索與dp的不同點:
dp需要對每個狀態進行遍歷,而記憶化搜索則可以排除無用狀態。更重要的是,記憶化搜索還可以剪枝,這樣一來,就大大降低了空間復雜度。
·接下來就是我調了半天的水題了:
相信大家一定都做過,這道題就是大名鼎鼎的“function”。
題目描述:
定義一個遞歸函數w(a,b,c),遞歸條件是這樣的:
(1)如果a \le 0a≤0 or b \le 0b≤0 or c \le 0c≤0就返回值11.
(2)如果a>20a>20 or b>20b>20 or c>20c>20就返回w(20,20,20)w(20,20,20)
(3)如果a<ba<b並且b<cb<c 就返回w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c)w(a,b,c−1)+w(a,b−1,c−1)−w(a,b−1,c)
(4)其它的情況就返回w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1)w(a−1,b,c)+w(a−1,b−1,c)+w(a−1,b,c−1)−w(a−1,b−1,c−1)
這是一個簡單的遞歸函數,但是實現起來可能會有些問題,比如當a、b、c都是15的時候調用的次數非常多,必須想一個辦法!
題目分析:
這個辦法就是“記憶化搜索”。根據(2)可以初步確定要將20以內的狀態存下來,這樣一來,每次遍歷到這個狀態時,就不用再去計算,直接返回已經存好的狀態就可以啦!
代碼實現:
1 #include<cstdio> 2 #include<iostream> 3 #define ll long long 4 5 using namespace std; 6 7 ll a,b,c; 8 ll ans[30][30][30]; 9 10 ll function(ll x,ll y,ll z) 11 { 12 if(x<=0||y<=0||z<=0) return 1; 13 if(x>20||y>20||z>20) return function(20,20,20); 14 if(ans[x][y][z]!=0) return ans[x][y][z];//如果這個情況已經被存在了ans數組中,那么就直接返回此狀態下數組的值 15 if(x<y&&y<z) ans[x][y][z]=function(x,y,z-1)+function(x,y-1,z-1)-function(x,y-1,z); 16 else return ans[x][y][z]=function(x-1,y,z)+function(x-1,y-1,z)+function(x-1,y,z-1)-function(x-1,y-1,z-1);//將各種狀態存到ans數組中 17 return ans[x][y][z]; 18 } 19 20 int main() 21 { 22 while(1)//如果不在循環中加上return 0,就會一直不停的讀下去 23 { 24 scanf("%lld%lld%lld",&a,&b,&c); 25 if(a==-1&&b==-1&&c==-1) return 0;讀到-1,-1,-1時直接結束程序 26 printf("w(%lld, %lld, %lld) = ",a,b,c); 27 if(a>20) a=21; 28 if(b>20) b=21; 29 if(c>20) c=21;//減少不必要的計算 30 printf("%lld\n",function(a,b,c)); 31 } 32 }
·總結:
記憶化搜索本質上是一種dp思想,但是又比dp快一點的算法,可以運用在在考試和平時做題時。
對了,還有一件事,一定要看好輸出格式,比如這道題我就沒有換行,導致WA了三次TwT。