对动态规划算法的理解及相关题目分析
1、对动态规划算法的理解
(1)基本思想:
动态规划算法的基本思想与分治法类似:将待求解的问题分解成若干个子问题,先求解子问题,然后从这些子问题的解中得到原问题的解。但是,与分治法不同的是,为了避免重复多次计算子问题,动态规划算法用一个表记录所有已解决的子问题的答案,不管该子问题以后是否被利用,只要它被计算过,就将其结果填入表中。
(2)设计动态规划算法的步骤:
①找出最优解的性质,并刻画其结构特征
②递归地定义最优值
③以自底向上的方式计算最优值
④根据计算最优值时得到的信息构造最优解
(3)动态规划算法的基本要素
①最优子结构性质
当问题的最优解包含了子问题的最优解时,称该问题具有最优子问题结构。在动态规划算法中,利用问题的最优子结构性质,以自底向上的方式递归地从子问题的最优解逐步构造出整个问题的最优解。
②重叠子问题性质
在用递归算法自顶向下解决问题时,每次产生的子问题并不总是新问题,有些子问题可能被反复计算多次。动态规划算法利用这种子问题的重叠性质,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只需要简单地用常数时间查看一下结果即可。
从一般意义上讲,问题所具有的最优子结构性质和重叠子问题性质是该问题可用动态规划算法求解的基本要素。
③备忘录方法
备忘录方法是动态规划算法的变形。与动态规划算法一样,备忘录方法用表格保存已解决的子问题的答案,在下次解决问题时,只要简单地查看该子问题的解答,不必再次计算。与动态规划算法不同的是,备忘录方法的递归方式是自顶向下的,而动态规划算法则是自底向上的。
备忘录方法为每个子问题建立一个记录项,初始化时,该记录项存入一个特殊的值,表示该子问题尚未求解。在求解的过程中对每个待求解的子问题,首先查看相应的记录项。若记录项存储的是初始化的特殊值,则需要计算该子问题的解,并保存在相应的记录项中已备以后查看。若记录项存储的不是初始值,则表示该子问题已被计算过,此时记录项存储的值即该子问题的解答。
2、例题的递归方程分析
(1)租用游艇问题:长江游艇俱乐部在长江上设置了n个游艇出租站1,2,…,n。游客可在这些游艇出租站租用游艇,并在下游的任何一个游艇出租站归还游艇。游艇出租站i到游艇出租站j之间的租金为r(i,j),1<=i<j<=n。试设计一个算法,计算出从游艇出租站1 到游艇出租站n所需的最少租金。
1 #include <iostream> 2 using namespace std; 3 int main() 4 { 5 int n;//n代表游艇出租站的数量 6 int cost[100][100];//cost[i][j]表示第i站到第j站所需要的租金 7 cin>>n; 8 for(int i=1;i<n;i++) { 9 for(int j=i+1;j<=n;j++){ 10 cin>>cost[i][j];//输入i->j站的价格 11 } 12 } 13 for(int m=2;m<n;m++) {//m表示要经过的出租站点 14 for(int i=1;i<=n-m;i++) {//i表示起始出租站,保证最后一段为m,所以循环截止到i<=n-m; 15 int j = i+m;//j表示要到达的出租站,而起始站为i,故j=i+m 16 for(int t=i+1;t<=j;t++) { 17 int temp = cost[i][t] + cost[t][j];//计算i->t站和t->j站租金之和 18 if(cost[i][j] > temp) //比较直接从i->j站的租金便宜还是从i->t、再从t->j站便宜 19 cost[i][j] = temp;//交换成较便宜的租金价格,直至最后第一排最后一个的值为最小租金 20 } 21 } 22 } 23 cout<<cost[1][n]; 24 return 0; 25 }
(2)单调递增最长子序列:设计一个O(n2)时间的算法,找出由n个数组成的序列的最长单调递增子序列的长度。
1 int longest(int a[], int n) 2 {//求最长单调递增子序列的长度,形参是长度为n的数组a 3 int length[n];//数组length[i]用于在数组a中记录比a[i]小的数的个数 4 int max = 1;//表示最长长度,初始化为1 5 for (int i = 0; i < n; i++){ 6 length[i] = 1;//数组length初始化为1 7 } 8 for (int i = 1; i < n; i++) { 9 for (int j = 0; j < i; j++) { 10 if (a[j] < a[i] && length[j] + 1 >length[i]) {//如果前一个数j小于后一个数i 11 length[i] = length[j] + 1;//则length数组中i对应位置的值变成j对应位置的值加1,表示存在多一个小于a[i]的数 12 if (max < length[i]) { 13 max = length[i];//将值赋给max 14 } 15 } 16 } 17 } 18 return max;//返回最长的长度 19 }
3、说明结对编程情况
一开始并不清楚怎么运用动态规划算法来分析和完成题目,于是和小伙伴一起认真地讨论题目的内容,研究了到底什么是动态规划算法,再尽量地去理解它的思想之后,和两个小伙伴一起完成这两道编程题。在这个过程中,照着自己的理解、小伙伴们的讨论以及借鉴了别的同学的建议和提醒,最终顺利地完成了任务。