生活随笔
收集整理的這篇文章主要介紹了
动态规划备忘录方法递归方法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
動態規劃的基本思想是,將原問題拆分為若干子問題,自底向上的求解。其總是充分利用重疊子問題,即通過每個子問題只解一次,把解保存在一個表中,巧妙的避免了子問題的重復求解。
遞歸方法,采用的是自頂向下的思想,拆分為若干子問題,但是造成了子問題的重復求解。
備忘錄方法,采用的也是自頂向下的思想,但是該方法維護了一個記錄子問題解的表,雖然填表動作的控制結構更像遞歸方法,但是的確避免了子問題的重復求解。
下面以字符串的相似度來展示一下各方法的特點:
動態規劃
遞歸:略
備忘錄:
[cpp]?view plaincopy
#include?<iostream>?? #include?<string>?? #include?<vector>?? using?namespace?std;?? ?? const?int?N?=?100;?? ?? int?dist[N][N];?? ?? int?minValue(int?va,?int?vb,?int?vc)?? {?? ????int?temp?=?va;?? ?? ????if(vb?<?temp)?? ????????temp?=?vb;?? ????if(vc?<?temp)?? ????????temp?=?vc;?? ?? ????return?temp;?? }?? ?? int?distance(string?strA,?int?pABegin,?int?pAEnd,?string?strB,?int?pBBegin,?int?pBEnd)?? {?? ????if(dist[pABegin][pBBegin]>=0)?? ????????return?dist[pABegin][pBBegin];?? ?? ????if?(pABegin?>?pAEnd)?? ????{?? ????????if(pBBegin?>?pBEnd)?? ????????????dist[pABegin][pBBegin]?=?0;?? ????????else?? ????????????dist[pABegin][pBBegin]?=?pBEnd?-?pBBegin?+?1;?? ?? ????????return?dist[pABegin][pBBegin];?? ????}?? ?? ????if?(pBBegin?>?pBEnd)?? ????{?? ????????if(pABegin?>?pAEnd)?? ????????????dist[pABegin][pBBegin]?=?0;?? ????????else?? ????????????dist[pABegin][pBBegin]?=?pAEnd?-?pBBegin?+?1;?? ?? ????????return?dist[pABegin][pBBegin];?? ????}?? ?? ????if?(strA[pABegin]==strB[pBBegin])?? ????{?? ????????dist[pABegin][pBBegin]?=?distance(strA,?pABegin+1,?pAEnd,?strB,?pBBegin+1,?pBEnd);?? ?? ????????return?dist[pABegin][pBBegin];?? ????}?? ????else?? ????{?? ????????int?t1?=?distance(strA,?pABegin,?pAEnd,?strB,?pBBegin+1,?pBEnd);?? ????????int?t2?=?distance(strA,?pABegin+1,?pAEnd,?strB,?pBBegin,?pBEnd);?? ????????int?t3?=?distance(strA,?pABegin+1,?pAEnd,?strB,?pBBegin+1,?pBEnd);?? ?? ????????dist[pABegin][pBBegin]?=?minValue(t1,?t2,?t3)+1;?? ?? ????????return?dist[pABegin][pBBegin];?? ????}?? ?????? }?? ?? int?main()?? {?? ????string?A;?? ????string?B;?? ?? ????cin>>A;?? ????cin>>B;?? ?? ????for?(int?i=0;?i<N;?i++)?? ????????for(int?j=0;?j<N;?j++)?? ????????????dist[i][j]?=?-1;?? ?? ????cout<<distance(A,?0,?A.length()-1,?B,?0,?B.length()-1);?? ????return?0;?? }??
當n=0時,f(n) = 0 ? ??
當n=1時,f(n) = 1
當n>1時,f(n) = f(n-1) + f(n-2)
遞歸算法:
[cpp]?view plaincopy
int?fun(int?n)?? {?? ????if(n?<=?0)?? ????????return?0;?? ????if(n?==?1)?? ????????return?1;?? ?? ????return?fun(n-1)+fun(n-2);?? }??
備忘錄方法:
[cpp]?view plaincopy
#include?<iostream>?? using?namespace?std;?? ?? const?int?N?=?100;?? int?f[N];?? ?? int?fun(int?n)?? {?? ????if(f[n]>=0)?? ????????return?f[n];?? ?? ????if(n?==?0)?? ????{?? ????????f[0]?=?0;?? ????????cout<<"0"<<endl;?? ????????return?f[0];?? ????}?? ?? ????if(n?==?1)?? ????{?? ????????f[1]?=?1;?? ????????cout<<"1"<<endl;?? ????????return?f[1];?? ????}?? ?? ????cout<<n<<endl;?? ????f[n]?=?fun(n-1)?+?fun(n-2);?? ????return?f[n];?? }?? ?? int?main()?? {?? ????for?(int?i=0;?i<N;?i++)?? ????????f[i]?=?-1;?? ?? ????cout<<fun(4);?? ????return?0;?? }??
由于計算的時候只需要前兩個數即可,所以代碼還可以繼續優化。但是對于上述的備忘錄方法貌似不能繼續進行空間優化了(不知道對否,如果理解的不對請不吝賜教~)。
但是對于下面的方法(就稱為遍歷方法吧),還是可以繼續優化的。
[cpp]?view plaincopy
#include?<iostream>?? using?namespace?std;?? ?? const?int?N?=?100;?? int?f[N];?? ?? int?main()?? {?? ?????? ????int?n;?? ????cin>>n;?? ?? ????for?(int?i=0;?i<=n;?i++)?? ????{?? ????????if(i==0)?? ????????????f[i]?=?0;?? ????????else?if(i==1)?? ????????????f[i]?=?1;?? ????????else?? ????????????f[i]?=?f[i-1]?+?f[i-2];?? ????}?? ?? ????cout<<f[n];?? ?? ????return?0;?? }??
由于計算的時候只用了前兩個數,所以沒有必要使用數組。
[cpp]?view plaincopy
#include?<iostream>?? using?namespace?std;?? ?? int?main()?? {?? ????int?n;?? ????cin>>n;?? ?? ????int?temp1,?temp2,?temp;?? ????for?(int?i=0;?i<=n;?i++)?? ????{?? ????????if(i==0)?? ????????????temp1?=?0;?? ????????else?if(i==1)?? ????????????temp2?=?1;?? ????????else?? ????????{?? ????????????temp?=?temp1?+?temp2;?? ????????????temp1?=?temp2;?? ????????????temp2?=?temp;?? ?????????????? ????????}?? ????}?? ?? ????cout<<temp;?? ?? ????return?0;?? }??
總結:從代碼中可以看出來,遍歷方法實際上是一種自底向上的方法,而備忘錄方法是一種自頂向下的方法,也許正由于這個原因造成了備忘錄方法無法進行空間優化。(待證)
?動態規劃算法的基本要素:?
1? 最優子結構性質
當問題的最優解包含了其子問題的最優解時,稱該問題具有最優子結構性質。
2? 重疊子問題性質???
動態規劃算法對每個問題只解一次,將其解保存在一個表格中,當再次需要解此問題時,用常數時間查看一下結果。因此,用動態規劃算法通常只需要多項式時間。
備忘錄方法:
?用一個表格來保存已解決的子問題的答案,用的時候查表即可。?
?采用的遞歸方式是自頂向下。
?控制結構與直接遞歸相同,區別在于備忘錄方式為每個解過的子問題建立備忘錄。?
?初始化為每個子問題的記錄存入一個特殊的值,表示并未求解。在求解過程中,查看相應記錄如果是特殊值,表示未求解,否則只要取出該子問題的解答即可。
備忘錄方法與動態規劃和遞歸的區別:
1、動態規劃是自低向上 ,備忘錄方法是自頂向下,遞歸是自頂向下
2、動態規劃每個子問題都要解一次,但不會求解重復子問題;備忘錄方法只解哪些確實需要解的子問題;遞歸方法每個子問題都要解一次,包括重復子問題??。
動態規劃解矩陣連乘問題
#include<iostream>
using namespace std;
void metrixchain(int n,int p[],int **s,int **m)
{
?for(int i=0;i<n;i++)
? m[i][i]=0;
?for(i=2;i<=n;i++)??? //小于等于n
?{
? for(int j=0;j<n-i+1;j++) //橫坐標
? { int k=j+i-1;?? //縱坐標
? m[j][k]=m[j+1][k]+p[j]*p[k+1]*p[j+1];s[j][k]=j;
? for(int t=j+1;t<k;t++)
? {
?? int u=m[j][t]+m[t+1][k]+p[j]*p[t+1]*p[k+1];
?? if(u<m[j][k])
?? {
??? m[j][k]=u;s[j][k]=t;
?? }
? }
? }
?}
}
void Traceback(int i, int j, int ** s)
{
?if (i==j||i==j-1) return;
?cout<<"divide after metrix "<<s[i][j]<<endl;
?Traceback(i, s[i][j],? s);
?Traceback(s[i][j]+1,j ,? s);
}
int main()
{
?int p[]={30,35,15,5,10,20,25};
?int **s=new int*[6];
?for(int i=0;i<6;i++)
? s[i]=new int[6];
?int **m=new int*[6];
?for( i=0;i<6;i++)
? m[i]=new int[6];
?metrixchain(6,p,s,m);
?for( i=0;i<6;i++)
?{ for( int j=i;j<6;j++)
?cout<<m[i][j]<<" ";
?cout<<endl;
?}
?Traceback(0,5,s);
?return 0;
}
總結
以上是生活随笔為你收集整理的动态规划备忘录方法递归方法的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。