学弟讲算法-动态规划-实例

动态规划思路

  • 原问题 子问题
  • 中间状态
  • 边界值
  • 状态转移方程

例1

数组最优解

从n个数的数组,取出若干个数两两不相邻的数,求取出的数最大和

  • 原问题 : n个数取出和最大的最优解

  • 子问题 : 前1个数的最优解,前2个数的最优解…前n个数的最优解…

  • 中间状态 : dp[i]代表前i个数的最优解,最终需求dp[n]

  • 边界值 : dp[1] = a[1] dp[2] = max(dp[1] ,a[2])

  • 状态转移方程 : 前i个数的最优解,重点是考虑加不加第i个数a[i];若不加可从dp[i-1]转移过来,若加只能从dp[i-2]转移过来(选择的数不能相邻)
    然后再取两种情况的最大值.因此: dp[i] = max( dp[i-1], dp[i-2]+a[i] ) {i>=2}

例2

连续数组最大和

在一个数组中a[n]有正数有负数,求取出的连续子数组中最大和

  • 原问题 : 前n个数中连续子数组最大和

  • 子问题 : 前1个数的最大和,前2个数的最大和…前n个数的最大和…

  • 中间状态 : dp[i]代表前i个数的最大和,最终需求dp[n]

  • 边界值 : dp[1] = a[1] dp[2] = max(dp[2] ,dp[2]+a[1])

  • 状态转移方程 : 对于状态i来说,若前一状态dp[i-1]为负数,则不需要前面的数,新状态为a[i];若dp[i-1]是正数,那就可以加上a[i]。关键就在于前面的状态还要不要加上
    dp[i]=max( a[i], dp[i-1]+a[i]) {i>=1}

例3

一个数组a[n],求出最长上升子序列的长度

  • 原问题 : 前n个数中最长上升子序列

  • 子问题 : 前1个数的最长上升子序列…前n个数的最长上升子序列…

  • 中间状态 : dp[i]代表前i个数的最长上升子序列,最终需求dp[n]

  • 边界值 : dp[1] = 1

  • 状态转移方程 : i = 2时,dp[2] = max(dp[1],dp[1]+1),即判断a[1]大于或小于a[2]的情况;对于状态i来说 dp[i] = max(dp[i],dp[j]+1) {j< i 并且 a[j] < a[i]}

for(int i = 2 ; i <= n ; i++){
    dp[i] = 1;
    for(int j = 1 ; j < i ; j++){
        if(a[j] < a[i]){
            dp[i] = max(dp[i],dp[j]+1);
        }
    }
}

例4

有面值为1,3,5的硬币若干枚,如何用最少的硬币凑够n元

  • 原问题 : 凑够n元所需最少硬币

  • 中间状态 : dp[i]代表凑够i元所需最少硬币,最终需求dp[n]

  • 边界值 : dp[1] = 1

  • 状态转移方程 : dp[i] = min(dp[i-1],dp[i-3],dp[i-5]) + 1

v[1] = 1,v[2] = 3,v[3] = 5;
for(int i = 1 ; i <= n ; i++){
    for(int j = 3 ; j >= 1 j--){
        if(i>v[j]){
            dp[i] = min(dp[i],dp[i-v[j]]+1)
        }
    }
}

例5

无向图G有个n结点及一些边,每条边带有正的权重值。找到结点1到结点n的最短路径

  • 原问题 : 找到节点1到节点n的最短路径

  • 中间状态 : dp[i]到节点i的最短路径,最终需求dp[n]

  • 边界值 : dp[1] = 0

  • 状态转移方程 : dp[i] = min(dp[i] , dp[j]+dis[i][j]) {dis[i][j] 代表j到i的距离}

MAX = 0x7fffffff;
dp[1] = 0;
for(int i = 1 ; i <= n ; i++){
    for(int j = 1 ; j < i ; j++){
        //若i能到j
        if(dis[i][j] < MAX){
            dp[i] = min(dp[i] , dp[j]+dis[i][j])
        }
    }
}

例6

平面上有n*m个格子,每个格子中放一定数量的苹果a[i][j],从左上角开始,每一步只能向下或向右走,走到一个格子获得格子内苹果,走到右下角最多能收集到多少个苹果

  • 中间状态 : dp[i][j]代表走到[i,j]时获得的苹果数
  • 边界值dp[i][1] = dp[i-1][1]+a[i][1] ; dp[1][j] = dp[i][j-1] +a[1][j]
  • 状态转移方程 : dp[i][j] = max(dp[i-1][j]+ dp[i][j-1])+a[i][j]
dp[1][1] = a[1][1]
for(int i = 1 ; i <=n ; i++){
    for(int j = 1 ; j <=n ; j++){
        if(i == 1){
            dp[i][j] = dp[i][j-1] + a[i][j]
        }
        else if(j == 1){
            dp[i][j] = dp[i-1][j] + a[i][j]
        }
        else{
            dp[i][j] = max(dp[i-1][j]+ dp[i][j-1])+a[i][j]
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值