生活随笔
收集整理的这篇文章主要介绍了
动态规划备忘录方法递归方法
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
动态规划的基本思想是,将原问题拆分为若干子问题,自底向上的求解。其总是充分利用重叠子问题,即通过每个子问题只解一次,把解保存在一个表中,巧妙的避免了子问题的重复求解。
递归方法,采用的是自顶向下的思想,拆分为若干子问题,但是造成了子问题的重复求解。
备忘录方法,采用的也是自顶向下的思想,但是该方法维护了一个记录子问题解的表,虽然填表动作的控制结构更像递归方法,但是的确避免了子问题的重复求解。
下面以字符串的相似度来展示一下各方法的特点:
动态规划
递归:略
备忘录:
[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;
}
总结
以上是生活随笔为你收集整理的动态规划备忘录方法递归方法的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。