动态规划初步

/**
*@author StormMaybin
*@Date 2016-9-3
*/

生命不息,奋斗不止!


动态规划概述

以下文字来源于网络

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。1957年出版了他的名著Dynamic Programming,这是该领域的第一本著作。
动态规划问世以来,在经济管理、生产调度、工程技术和最优控制等方面得到了广泛的应用。例如最短路线、库存管理、资源分配、设备更新、排序、装载等问题,用动态规划方法比用其它方法求解更为方便。
  虽然动态规划主要用于求解以时间划分阶段的动态过程的优化问题,但是一些与时间无关的静态规划(如线性规划、非线性规划),只要人为地引进时间因素,把它视为多阶段决策过程,也可以用动态规划方法方便地求解。
  动态规划程序设计是对解最优化问题的一种途径、一种方法,而不是一种特殊算法。不象前面所述的那些搜索或数值计算那样,具有一个标准的数学表达式和明确清晰的解题方法。动态规划程序设计往往是针对一种最优化问题,由于各种问题的性质不同,确定最优解的条件也互不相同,因而动态规划的设计方法对不同的问题,有各具特色的解题方法,而不存在一种万能的动态规划算法,可以解决各类最优化问题。因此读者在学习时,除了要对基本概念和方法正确理解外,必须具体问题具体分析处理,以丰富的想象力去建立模型,用创造性的技巧去求解。我们也可以通过对若干有代表性的问题的动态规划算法进行分析、讨论,逐渐学会并掌握这一设计方法。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

经典01背包问题

题目

一个旅行者准备随身携带一个背包,可以放入背包的物品有n种,每种物品的重量和价值分别为w[i], v[i] . 如果背包的最大重量限制是b, 怎样选择放入背包的物品以使得背包的价值最大?

算法分析

对于每一种特定的物品来说,它只有两个状态,要么被装了,要么就是没有被装,所以把这种只有两种状态的背包问题就叫做是01背包问题!那么我们现在给出一组数据来分析:
b = 10,w[6]={0,2,2,6,5,4},物品价值V[6]={0,6,3,5,4,6}(前导0是为了方便计算和理解)。

以下图片分析来自孤剑独舞

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

这个分析应该是最详细的01背包讲解了,那么现在给出状态转移方程:
*dp[i][v] = max (dp[i-1][v], dp[i-1][v-w[i]]+v[i])*dp[i][v]表示前i个物品,背包容量是v的时候最大的价值,那么dp[i][v]就和第i个物品装不装有关系,如果第i个物品不装那么最大价值是dp[i-1][v],如果装的话那么就是dp[i-1][v-w[i]]+v[i]。很好理解吧 !

package com.stormma.zeroOnePack;
import java.util.Scanner;
public class ZeroOnePack
{
    Scanner scan = new Scanner (System.in);
    public ZeroOnePack()
    {
        //物品个数
        int count;
        //背包最大容量
        int weight;
        //每一个物品的重量
        int [] w;
        //每一个物品的价值
        int [] v;
        while (scan.hasNext())
        {
            weight = scan.nextInt();
            count = scan.nextInt();
            w = new int [count+1];
            v = new int [count+1];
            for (int i = 1; i <= count; i++)
                w[i] = scan.nextInt();
            for (int i = 1; i <= count; i++)
                v[i] = scan.nextInt();
            int [][] dp = new int[count+1][weight+1];
            dp[0][0] = 1;
            for (int i = 1; i <= count; i++)
            {
                for (int j = 1; j <= weight; j++)
                {
                    //如果可以装下
                    if (j >= w[i])
                        dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
                }
            }
            System.out.println(dp[count][weight]);
        }
    }
    public static void main(String[] args)
    {
        // TODO Auto-generated method stub
        new ZeroOnePack();
    }
}

数字三角形

题目描述

有一个由非负整数组成的三角形,第一行只有一个数,除了最下行之外每个数的左下方和右下方都有一个数,如下图
这里写图片描述
要求从第一个数开始,每次可以往右下方或者左下方走一格,直到走到最下行,把沿途经过的数全部加起来,如何走才能使得这个和最大?

分析

定义状态dp[i][j]为从格子(i,j)出发时能得到的最大和(包括(i,j)本身的值),那么我们最终的结果应该是dpp[1][1],从格子(i,j)出发有两种决策,即往右下方或者是左下方,如果往左下方走,那么必须要求dp[i+1][j]是最大的,反之如果向右下方那么要求dp[i+1][j+1]应该是最大的。那么我们可以得到状态转移方程:
dp[i][j] = a[i][j] + max(d[i+1][j],dp[i+1][j+1])这个状态转移说明如果往左下方走那么最好情况就是a[i][j]+从(i+1,j)开始走的最大和,反之如果向右下方那么最好情况就是a[i][j] + 从(i+1, j+1)开始走的最大值!
有了状态转移方程,我们用递归形式表示如下:

递归形式
public int dp(int i, int j)
{
    return a[i][j] + (i == n) ? 0 : Math.max(dp(i+1, j), dp(i+1, j+1));
}

上面这个递归形式是正确的,但是出于时间效率的考虑,我们不得不寻找更快解法!

递推形式
int i, j;
//底层赋值
for (j = 1; j <= n; j++)
{
    dp[n][j] = a[n][j];
}
for (i = n-1; i >= 1; i--)
{
    for (j = 1; j <= i; j++)
    {
        d[i][j] = a[i][j] + Math.max(dp(i+1, j), dp(i+1, j+1));
    }
}
记忆化搜索

这个就是比较快速的解法了

Arrays.fill(dp,-1);
public int d(int i, int j)
{
    //记忆化
    if (dp[i][j] > 0)
        return dp[i][j];
    return dp[i][j]  = a[i][j] + (i == n) ? 0 : Math.max(dp(i+1, j), dp(i+1, j+1));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值