动态规划理解

马尔科夫模型

对于Ai+1,只需要考察前一个状态Ai即可,只要状态Ai确定,则计算Ai+1时不需要考察前面的的状态A1…Ai-1,在图论中,常称之为马尔科夫模型

高阶马尔科夫模型

对于Ai+1,需要考察前面i个状态集{A1…Ai-1Ai}才能完成整个推理过程,称之为高阶马尔科夫模型。
在计算机算法中,高阶马尔科夫模型的推理叫做“动态规划”,马尔科夫模型的推理,对应“贪心法”。

动态规划

无论是动态规划还是贪心算法,都是根据A0…i,计算Ai+1的过程。

  • 计算Ai+1不需要Ai+2…Ai+7…
  • 一旦Ai+1计算完成之后,后面就不需要再次计算Ai+2、Ai+3…,并且Ai+1的值不会更改了
  • 就叫做无后效性
    后者这样理解动态规划:计算Ai+1,只需要知道A[0…i]的值,无需知道A[0…i]是通过何种途径计算得到的——只需要知道它们当前的状态值本身。如果A[0…i]的全体作为一个整体,可以认为动态规划是马尔科夫过程,而非高阶马尔科夫模型。

例1:最长递增子序列LIS,给定一个长度为N的数组,找出一个最长单调的自增子序列,不要求是连续的。例如:6 5 7 8 4 3 9 1,最长递增子序列是{ 6, 7, 8, 9}{5, 7, 8, 9}。可以看出最长递增子序列不唯一,但是长度一定是唯一的,不然也不能叫最长递增子序列。
记以第i个数组元素ai结尾的最长递增子序列的长度为dp[i],子序列记为Li,ai是要缀到L0L1…Li-1的后面的条件:

  • 如果aj<ai,则可以将ai缀到Lj后面,使得Lj长度增加
    故:dp[i]={max(dp[j]+1,0<=j<i并且a[i]<=a[j])}
  • 遍历在i之前所有的位置,找出满足条件的a[i]<=a[j]的a[j]
  • 计算得到dp[0…n-1]后,遍历所有的dp[i],找出最大的长度
  • dp[i] 默认都为 1,因为以 i 结尾的 LIS 至少包含自己。
    (1) 6 5 7 8 4 3 9 1 dp[0]=1
    (2)6 5 7 8 4 3 9 1 处理第二个元素5的时候判断是否比前面的元素 6大,没有的话那么以 5 为结尾的 LIS 就是 5本身,即 LIS 长度为 1,dp[1]=1
    (3)6 5 7 8 4 3 9 1 处理第三个元素7的时候需要跟前面的每个元素都进行比较,7大于 6,5 则 LIS 的长度可能为 dp[2]={max(dp[0]+1,dp[1]+1)} =2,以此类推,得到最长序列为4。
void LIS()
{
    for(int i = 0; i < n; i++)//初始化dp[i] = 1;
    {
        dp[i] = 1;
    }
    for(int i = 1; i < n; i++)//遍历
    {
        for(int j = 0; j < i; j++)//从i前面的查找
        {
            if(a[i] > a[j] && dp[i] < dp[j] + 1)//dp[i] < dp[j] + 1的作用就是更新最大的一个递增
                dp[i] = dp[j] + 1;//max(dp[j]+1,0<=j<i并且a[i]<=a[j])
        }
    }
}

何时考虑使用动态规划

  • 初始规模下能够方便得出结论:空串、长度为0的数组、自身(LIS=1)等——初始条件
  • 能够得到问题规模增大导致的变化:递推式——状态转移方程
  • 无后效性

例2:字符串的交替连接,输入三个字符串s1、s2和s3,判断s3是否是前两个字符串交错而成,s1∪s2=s3。
状态转移函数:

  • dp[i,j]表示s3[1…i+j]是否由s1[1…i]和s2[1…j]的字符组成,取值为true/false
  • 如s1[i]==s3[i+j],且dp[i-1,j]为真,那么dp[i,j]为真,因为s1和s3末尾相等,追上一个s1[i]还可以相等
  • 如s2[j]==s3[i+j],且dp[i,j-1]为真,那么dp[i,j]为真,因为s2和s3末尾相等,追上一个s2[j]还可以相等
  • 其他情况,dp[i,j]为假
    初始条件:dp[0,0]=true,空串可以由空串组成
 */
    bool isInterleave(string s1, string s2, string s3) {
    	int n=s1.length(),m=s2.length(),s=s3.length();
    	//如果长度不一样,则s1∪s2不可能为s3
        if(s!=m+n)  
            return false;  
        if(n==0)  
            return s2==s3;  
        if(m==0)  
            return s1==s3;  
        vector<vector<bool> > dp(n+1,vector<bool>(m+1,false));  
        dp[0][0] = true; //空串可以由空串组成 
        for(int i=0;i<n+1;i++)  
        {  
            for(int j=0;j<m+1;j++)  
            {  
            	if(i-1>=0&&dp[i-1][j]==true&&s1.charAT(i-1)==s3.charAT(i+j-1)||j-1>=0&&dp[i][j-1]==true&&s2.charAT(j-1)==s3.charAT(i+j-1))
                	dp[i][j]=true; 
                else
                	dp[i][j]=false; 
                   
            }  
        }  
        return dp[n][m];  

例3:走棋盘/格子取数,给定m*n的矩阵,每个位置是一个非负数,从左上角开始,每次只能朝下和右边走,走到右下角,求总和最小的路径。
初始条件:dp[0][0]=a[0][0]
状态转移函数:dp[x][y]=max(dp[x-1][y]+a[x][y],dp[x][y-1]+a[x][y]),即dp[x][y]=max(dp[x-1][y],dp[x][y-1])+a[x][y]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值