Dynamic Programming动态规划之收集苹果

动态规划(英语:Dynamic programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。

动态规划常常适用于有重叠子问题[1]和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。

以下部分介绍内容来源于
http://www.cnblogs.com/lihonglin2016/p/4298432.html

平面上有N*M个格子,每个格子中放着一定数量的苹果。从左上角的格子开始, 每一步只能向下走或是向右走,每次走到一个格子就把格子里的苹果收集起来, 这样一直走到右下角,问最多能收集到多少个苹果。

不妨用一个表格来表示:

{5, 8, 5, 7, 1, 8},
{1, 3, 2, 8, 7, 9},
{7, 8, 6, 6, 8, 7},
{9, 9, 8, 1, 6, 3},
{2, 4,10, 2, 6, 2},
{5, 5, 2, 1, 8, 8},

在这个6X6的表格里面填写了一些数表示所在格子中的苹果数量。根据题目的规则”每一步只能向下走或是向右走”,如果用x表示纵向,用y表示横向,那么能够到达a[x,y]处的只有两个位置a[x-1, y] (上一格)和a[x,y-1](左边一格),所以必然是取这两个位置中比较大的那一个点。依此回溯到a[0,0],或者从a[0,0]递推到a[x,y]。

……… , ……… , a[x-1,y]

……… , a[x,y-1], a[x,y] ,

基于这一点,我们可以从左上角开始将到达第一行和第一列中各点所能收集到的最大苹果数量填成一张表格。如下:

这里写图片描述

接下来填第2行,首先是第2行第2列的值,应该填写为 MAX(A[1,2], A[2,1])+ A[2,2]对应的苹果数量。也就是说到达第2行第2列能获得的最大苹果数,要看第2行第1列所获得的苹果数(6)和第1行第2列所获得的苹果数(13),这两者哪个更大,谁大就取谁的值,显然第1行第2列所获得的苹果数(13)更大,所以用13加上第2行第2列的苹果数3 = 16,就是到达第2行第2列能获得的最大苹果数。同理,填所在格能获得的最大苹果数就是看它左面一格和上面一格哪个值更大,就取哪个值再加上自己格子里面的苹果数,就是到达此格能获得的最大苹果数。依此填完所有格子,最后得到下图:
这里写图片描述

所以:到达右下角能够获得的最大苹果数量是76。所经过的路径可以通过倒推的方法得到,从右下角开始看所在格子的左边一格和上面一格哪边大就往哪边走,如果遇到一样大的,任选一条即可。

这样我们可以画出路线图,如下图右边表格:

这里写图片描述

这个例子的分析和解决方法大概就是这样了。在前面第一个例子里面我们提到:空间换时间是动态规划的精髓。但是一个问题是否能够用动态规划算法来解决,需要看这个问题是否能被分解为更小的问题(子问题)。而子问题之间是否有包含的关系,是区别动态规划算法和分治法的所在。一般来说,分治法的各个子问题之间是相互独立的,比如折半查找(二分查找)、归并排序等。而动态规划算法的子问题在往下细分为更小的子问题时往往会遇到重复的子问题,我们只处理同一个子问题一次,将子问题的结果保存下来,这就是动态规划的最大特点。

动态规划算法总结起来就是两点:

1 寻找递推(递归)关系,比较专业的说法叫做状态转移方程。

2 保存中间状态,空间换时间。

public class Main {

    static int grid[][] =
            {{5, 8, 5, 7, 1, 8},
            {1, 3, 2, 8, 7, 9},
            {7, 8, 6, 6, 8, 7},
            {9, 9, 8, 1, 6, 3},
            {2, 4, 10, 2, 6, 2},
            {5, 5, 2, 1, 8, 8}};

    static int value[][] =
            {{5, 8, 5, 7, 1, 8},
            {1, 3, 2, 8, 7, 9},
            {7, 8, 6, 6, 8, 7},
            {9, 9, 8, 1, 6, 3},
            {2, 4, 10, 2, 6, 2},
            {5, 5, 2, 1, 8, 8}};

    public static void main(String[] args) {
        // write your code here
        test();
    }

    static public void test(){
        dp(0, 0);
        int x = 5;
        int y = 5;
        int left;
        int up;
        List<String> list = new ArrayList<>();
        list.add("[" + x + "," + y + "]");
        for(int i=0; i<6; i++){
            for(int j=0; j<6; j++){
                if(x == 0){
                    y--;
                }else if(y == 0){
                    x--;
                }else {
                    left = value[x][y - 1];
                    up = value[x - 1][y];
                    if (left > up) {
                        y = y - 1;
                    } else {
                        x = x - 1;
                    }
                }
                list.add(0,"[" + x + "," + y + "]");
                if(x ==0 && y == 0){
                    System.out.println(Arrays.deepToString(list.toArray()));
                    return;
                }
            }
        }
    }
    static public void dp(int x, int y){
        value[x][y] = grid[x][y];
        int max = 0;
        int n = 6;
        //from top
        if(x > 0 && max < value[x-1][y]){
            max = value[x-1][y];
        }
        //from left
        if(y > 0 && max < value[x][y-1]){
            max = value[x][y-1];
        }
        value[x][y] += max;
        if(x < n-1){
            dp(x+1, y);
        }
        if(y < n-1){
            dp(x, y+1);
        }
    }

}

输出结果如下:
[[0,0], [0,1], [0,2], [0,3], [1,3], [1,4], [2,4], [3,4], [4,4], [5,4], [5,5]]

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值