動態規劃&備忘錄方法&遞迴方法
動態規劃的基本思想是,將原問題拆分為若干子問題,自底向上的求解。其總是充分利用重疊子問題,即通過每個子問題只解一次,把解儲存在一個表中,巧妙的避免了子問題的重複求解。
遞迴方法,採用的是自頂向下的思想,拆分為若干子問題,但是造成了子問題的重複求解。
備忘錄方法,採用的也是自頂向下的思想,但是該方法維護了一個記錄子問題解的表,雖然填表動作的控制結構更像遞迴方法,但是的確避免了子問題的重複求解。
下面以字串的相似度來展示一下各方法的特點:
遞迴:略
備忘錄:
- #include <iostream>
- #include <string>
-
#include <vector>
- usingnamespace std;
- constint 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
- {
- 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
- }
- 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)
遞迴演算法:
- int fun(int n)
- {
- if(n <= 0)
- return 0;
- if(n == 1)
- return 1;
- return fun(n-1)+fun(n-2);
- }
備忘錄方法:
- #include <iostream>
- usingnamespace std;
- constint 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;
- }
由於計算的時候只需要前兩個數即可,所以程式碼還可以繼續優化。但是對於上述的備忘錄方法貌似不能繼續進行空間優化了(不知道對否,如果理解的不對請不吝賜教~)。
但是對於下面的方法(就稱為遍歷方法吧),還是可以繼續優化的。
- #include <iostream>
- usingnamespace std;
- constint N = 100;
- int f[N];
- int main()
- {
- int n;
- cin>>n;
- for (int i=0; i<=n; i++)
- {
- if(i==0)
- f[i] = 0;
- elseif(i==1)
- f[i] = 1;
- else
- f[i] = f[i-1] + f[i-2];
- }
- cout<<f[n];
- return 0;
- }
- #include <iostream>
- usingnamespace std;
- int main()
- {
- int n;
- cin>>n;
- int temp1, temp2, temp;
- for (int i=0; i<=n; i++)
- {
- if(i==0)
- temp1 = 0;
- elseif(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;
}