动态规划经典例题
2015-11-02 20:02
323 查看
关于动态规划的介绍很多,本文希望通过重复几个最经典的例题来理解动态规划。
1.最优子结构:当问题的最优解包含了子问题的最优解时,称该问题具有最优子结构。
2.重叠子问题:在递归求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。
动态规划利用子问题的重叠性质,对每一个子问题只解一次,而后将其保存在一个表格中,当再次需要解决此子问题是,只需要简单的常数时间查看结果。
(2)递归的定义最优值。
(3)以自低向上的方式计算出最优值。
(4)根据计算最优值得到的信息,构造最优解。
事实上,最长公共子序列问题具有最优子结构性质。
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={
z1,z2,…zk},则
(1)若xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Ym-1的最长公共子序列。
(2)若xm!=yn,且zk!=xm,则Zk-1是Xm-1和Y的最长公共子序列。
(3)若xm!=yn,且zk!=yn,则Zk-1是Yn-1和Y的最长公共子序列。
令c[i][j]表示序列Xi和Yj的最长公共子序列的长度。
则由最优子结构性质可以建立递归关系如下:
1)c[i][j]=0;//当i=0,j=0
2)c[i][j]=c[i-1][j-1]+1//当i,j>0;xi=yj
3)c[i][j]=max{c[i][j-1],c[i-1][j]}//i,j>0;xi!=yj
代码实现
分析:
令状态方程p[i][j]=0表示起始位置为i,结束位置为j的字符串不为回文,
p[i][j]=1,表示此回文。
状态转移方程为
p[i][j]=
{
p[i][j]=1;//若p[i+1][j-1]==1且str[i]==str[j];
p[i][j]=0//其他
}
代码实现
1.为什么要用动态规划?
1.1动态规划解决的是哪类问题?
与分治法不同,适用于动态规划求解的问题经分解得到的子问题往往不是相互独立的。1.2什么情况下能用动态规划?
使用动态规划需要满足以下性质1.最优子结构2.重叠子问题1.最优子结构:当问题的最优解包含了子问题的最优解时,称该问题具有最优子结构。
2.重叠子问题:在递归求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。
1.3为什么要用动态规划
动态规划能提高解决上述这类问题的效率。动态规划利用子问题的重叠性质,对每一个子问题只解一次,而后将其保存在一个表格中,当再次需要解决此子问题是,只需要简单的常数时间查看结果。
2.设计动态规划算法的步骤
(1)找出最优解的性质,并刻画其结构特征。(2)递归的定义最优值。
(3)以自低向上的方式计算出最优值。
(4)根据计算最优值得到的信息,构造最优解。
3.经典例题
问题1.求最长公共子序列
思路事实上,最长公共子序列问题具有最优子结构性质。
设序列X={x1,x2,…,xm}和Y={y1,y2,…,yn}的最长公共子序列为Z={
z1,z2,…zk},则
(1)若xm=yn,则zk=xm=yn,且Zk-1是Xm-1和Ym-1的最长公共子序列。
(2)若xm!=yn,且zk!=xm,则Zk-1是Xm-1和Y的最长公共子序列。
(3)若xm!=yn,且zk!=yn,则Zk-1是Yn-1和Y的最长公共子序列。
令c[i][j]表示序列Xi和Yj的最长公共子序列的长度。
则由最优子结构性质可以建立递归关系如下:
1)c[i][j]=0;//当i=0,j=0
2)c[i][j]=c[i-1][j-1]+1//当i,j>0;xi=yj
3)c[i][j]=max{c[i][j-1],c[i-1][j]}//i,j>0;xi!=yj
代码实现
#include <stdio.h> #include <stdlib.h> #include <string.h> #define M 100 #define N 100 int c[M][N];// 用于保存Xi和Yi的最长公共子序列的长度 char lcstr[10000]; int lcsLength(char strx[],char stry[],int b[][N])//b[i][j]用于保存c[i][j]由哪一个子问题得到的 { int m,n,i,j; m=strlen(strx); n=strlen(stry); for(i=0;i<=m;i++) { c[i][0]=0; } for(i=1;i<=n;i++) { c[0][i]=0; } for(i=1;i<=m;i++)//用迭代 { for(j=1;j<=n;j++) { if(strx[i-1]==stry[j-1]) { c[i][j]=c[i-1][j-1]+1; b[i][j]=1; }else if(c[i-1][j]>=c[i][j-1]) { c[i][j]=c[i-1][j]; b[i][j]=2; }else{ c[i][j]=c[i][j-1]; b[i][j]=3; } } } return c[m] ; } //构造最长公共子序列,此函数打印最大公共子序列 void lcs(int i,int j,char strx[],int b[] ) { if(i==0||j==0) { if(b[0][0]==1) { printf("%c",strx[0]); } return ; } if(b[i][j]==1){ lcs(i-1,j-1,strx,b); printf("%c",strx[i-1]); }else if(b[i][j]==2) { lcs(i-1,j,strx,b); }else { lcs(i,j-1,strx,b); } } int main() { char x[]="abc"; char y[]="bc"; int record[M] ; int result=lcsLength(x,y,record); printf("%d\n",result); int strlenx=strlen(x); int strleny=strlen(y); int i,j; for(i=0;i<strlenx;i++) { for(j=0;j<strleny;j++) { printf("%d ",record[i][j]); } printf("\n"); } lcs(strlenx,strleny,x,record); return 0; }
问题2 求一个字符串中的最长的回文子串
回文是指正着读和倒着读,结果一样,比如abcba或abba。分析:
令状态方程p[i][j]=0表示起始位置为i,结束位置为j的字符串不为回文,
p[i][j]=1,表示此回文。
状态转移方程为
p[i][j]=
{
p[i][j]=1;//若p[i+1][j-1]==1且str[i]==str[j];
p[i][j]=0//其他
}
代码实现
#include <iostream> #include <string> using namespace std; string longestPalindrome(string &str) { int length=str.size(); int start,maxlen;//最大子字符串的起点位置和长度 int len;//长度的临时变量 int p[100][100]={false}; for(int i=0;i<length;i++) { p[i][i]=1; if(p[i][i]&&str[i]==str[i+1]) { p[i][i+1]=true; maxlen=2; start=i; } } //len从1开始还是从0开始? //从0开始,否则当i=0,len=str.size(),i+len会越界,所以len的取值区间[0,length-1] for(len=2;len<length;len++) { for(int i=0;i<length-1-len;i++)//当长度为len+1,起点i的位置最大位置length-(len+1)-1 { int j=i+len; if(p[i+1][j-1]&&str[i]==str[j]) { p[i][i+len]=1; maxlen=len+1; start=i; }else{ p[i][i+len]==0; } } } if(maxlen>2){ return str.substr(start,maxlen); } return NULL; } int main() { string str="auuabab"; cout<<longestPalindrome(str); return 0; }
相关文章推荐
- ThinkPHP 学习每日总结
- 大数据读书笔记(1)
- 11.1
- HDU 5534 Partial Tree
- Largest Rectangle in Histogram的几个解法
- 如何在导航栏的左右两侧添加多个Button
- poj 2186 Popular Cows
- ThinkPHP创建应用的一般开发流程及实例
- ubuntu 14.04 使用zram
- IAR6.3 中对 CMSIS 的处置
- android组件之一Service
- 11.2
- Xcode全局断点
- 使用libxml读取分析配置文件
- CentOS6.7源码搭建LNMP平台
- 当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的.
- Deep learning系列(十五)有监督和无监督训练
- POJ1062昂贵的聘礼
- Swift的下标代码
- hdu 1827 Summer Holiday