【算法】动态规划DP浅析(1)

动态规划(Dynamic Programming, DP)

1. 基本思想

定义

所谓动态规划是指,问题有多个可行解,每个可行解对应一个目标值,目的就是要在可行解中求出最优值(最大或者最小)

  • 目标:通常用于求解某种具有最优性质的问题
  • 基本思路:将待求解的问题先分解成若干个子问题,先求解子问题,然后从这些子问题中得到原问题的解

和分治的不同

  • 适用于动态规划求解的问题,经过分解得到的子问题往往是不相互独立的。
  • 我们用一个表来记录所有已经求解的子问题的答案。不管子问题的解是否在后来被用到,只要是计算过的,就存储在表中。
    具体的动态规划问题多种多样,但是它们都具有相同的填表结构

2. 设计动态规划的方法步骤

     1. **找出最优解的性质,并刻画其结构特征;**
     2. **递归地定义最优值(写出动态规划方程);**
     3. **自底而上地方式计算出最优值;**
     4. **根据最优值得到的性质,构造一个最优解;**

3. 动态规划的特征

  • 最优子结构性质:当问题的最优解包含其子问题的最优解时,称该问题具有最优子结构性质
  • 重叠子问题 :对于每个子问题只解一次,然后将其解保存在一个表格中,以后尽可能用这些子问题的解

矩阵链乘法

void matixChain(int n,int **p,int **m,int **s)
{
    for(int i=1;i<=n;i++)//表m中存储dp子问题的解 
    m[i][i]=0;//当矩阵链中只有一个矩阵时的平凡情况,故为0 
    for(int r=2;r<=n;r++)
    for(int i=1;i<=n-r+1;i++)//形成上三角结构表 
    {
        int j=i+r-1;//计算初值,从i处断开
        m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];//向量P中存储的是各个行数 
        s[i][j]=i;//保存加括号的位置 
        for(int k=i+1;k<j;k++)//处理的范围是从编号为i的矩阵到编号为j的矩阵链的练成问题 
        {
            int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
            if(t<m[i][j])
            {
                m[i][j]=t;//储存子问题的最优解 
                s[i][j]=k;//保存子问题取得最优解时矩阵链在哪里加的括号 
            }
        } 

    } 

}

void trackBack(int i,int j,int **s) //构造最优解 
{
    if(i==j)return ;
    trackBack(i,s[i][j],s);
    trackBack(s[i][j]+1,j,s);
    printf("Multiply A %d,%d",i,s[i][j]);
    printf("and A%d, %d",s[i][j]+1,j);
}

例2,ACM经典问题——树塔
概述:给出一个数字三角形,编写一个程序,计算出从顶至底的某处的一条路径,使得该路径所经过的数字的总和最大。

#include <iostream>
#include <cstdio>
#include <algorithm> 
using namespace std;
const int MAXN=111;
int dp[MAXN][MAXN]={0};
int main()
{
    int T,n;
    scanf("%d",&T)  ;
    while (T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            for (int j=1;j<=i;j++){
                scanf("%d",&dp[i][j]);//构造一个树塔 
                printf("Old dp[%d][%d]=%d\n",i,j,dp[i][j]);
            }
        }
        for(int i=n-1;i>=1;i--){
            for (int j=1;j<=i;j++){     
                dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]);//状态转移方程 
                printf("New dp[%d][%d]=%d\n",i,j,dp[i][j]);
            }
        }
        printf("%d\n",dp[1][1]);
    }
    return 0;
}

*结题报告*
如果自上而下考虑,需要深度搜索,查找每一条路径,并记录数值,比较大小。
采取自下而上考虑,从最基本的两层树塔n==2为模型,两个点比较大小即可;再看n==3,除掉塔顶,就可以看作是两个n==2时的情况。如果确定走某一棵树(n==3),必然会走这棵树上的最大的路径,所以只要得到两棵n==2的树的最大路径,分别进行比较,就能得到n==3树上的最大路径;
同理,n==4,…
核心:状态转移方程,dp[i][j]+=max( dp[i+1][j], dp[i+1][j+1] )
我将这个树塔倒置,

4        5        2        6        5            button
    2        7        4        4
          8        1        0
                3        8
                    7                                top

按照算法执行:

i=4
4        5        2        6        5            button
    '7'     '12'     '10'     '10'               i=4
          8        1        0                      i=3
                3        8                          i=2
                    7                                top
i=3
4        5        2        6        5            button
    7      12      10       10                 i=4
       '20'      '13'      '10'                 i=3
                3        8                        i=2
                    7                              top
i=2
4        5        2        6        5            button
    7      12      10       10                 i=4
          20       13      10                   i=3
              '23'      '21'                         i=2
                    7                                top
i=1
4        5        2        6        5            button
    7      12      10       10                 i=4
          20       13      10                   i=3
              23      21                          i=2
                   '30'                              top

将max改为min就可以求最小路径

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值