Java练习:动态规划

目标:实现下面游戏的最优路线

规则
  1. 每个方格的数字代表一个分数值,争取从START开始以最大分数值到达END
  2. 只能有三种移动方式:向下、向右、向右下
    在这里插入图片描述
代码思路
  1. 将思维放在如何在每一步取得当前格可能获得的最大分数值。如下图所示:
    根据规则的走法,右下角的值只有3种可能:5、9、13,因此取13。
    在这里插入图片描述
    2、为了计算放在边缘的值与计算不是在边缘的值使用同一套算法,我们可以增加1行与1列做哨兵,如下图所示:
    在这里插入图片描述
    3、计算出最大分数值后,进行终点逆推得出路线,因此每个坐标都需要记录分数与上一个坐标位置。最佳答案如下:
    在这里插入图片描述

代码实现:

package com.miracle.study.dynamic;

import org.junit.jupiter.api.Test;

import java.util.LinkedList;
import java.util.List;

/**
 * @author Miracle
 * @date 2021/4/10 16:58
 */
public class BestPath {

    public List<int[]> bestChoice(int[][] map){
        var ml = map.length;
        // 重设地图,需要在地图上与左边铺哨兵,3表示{分数,上一个x位置,上一个y位置}
        var dp = new int[ml +1][ml + 1][3];
        // 哨兵的值需要足够的小并且一致
        for (int i = 1; i < ml + 1; i++){
            dp[i][0][0] = -1000;
            dp[0][i][0] = -1000;
        }

        // 循环y轴
        for (int y = 1; y < ml + 1; y++){
            // 循环x轴
            for (int x = 1; x < ml + 1; x++){
                // 暂定左上节点为最优节点
                var max = dp[x - 1][y - 1][0];
                var dx = x - 1;
                var dy = y - 1;

                // 判断上方节点的分数值情况,如果比当前最大分数值大,就更换为最优节点
                if (dp[x][y - 1][0] > max){
                    max = dp[x][y - 1][0];
                    dx = x;
                }

                // 判断左方节点的分数值情况,如果比当前最大分数值大,就更换为最优节点
                if (dp[x - 1][y][0] > max){
                    max = dp[x - 1][y][0];
                    dy = y;
                }

                // 计算当前节点的分数
                dp[x][y][0] = max + map[x-1][y-1];
                // 设置上一个最优节点坐标
                dp[x][y][1] = dx;
                dp[x][y][2] = dy;

                // 打印操作记录
                System.out.format("fill:dp=%d, px=%d, py=%d\n", dp[x][y][0], dx, dy);
            }
        }
        // 循环打印每个点的最优值
        for (int x = 0; x < ml + 1; x++){
            for (int y = 0; y < ml + 1; y++){
                System.out.format("%7d ",dp[x][y][0]);
            }
            System.out.println();
        }

        int x = ml, y = ml;
        // 记录路线坐标
        var path = new LinkedList<int[]>();
        // 先记录终点 ,坐标减一的目的是为了对应回map
        path.addFirst(new int[]{x - 1, y - 1});
        // 从终点逆推会始点记录坐标
        while (true){
            int dx = dp[x][y][1];
            int dy = dp[x][y][2];
            x = dx;
            y = dy;
            // 判断是否已经到达始点
            if (x == 0 && y == 0){
                break;
            }
            path.addFirst(new int[]{x - 1, y - 1});
        }
        return path;
    }

    @Test
    public void test(){
        var map = new int[][]{
                {5, 4, 2, 2},
                {8, 0, 5, 7},
                {4, 1, 2, 0},
                {1, 4, 6, 3}
        };
        var path = bestChoice(map);
        // 循环将坐标点对应回map中
        for (int[] i : path){
            var x = i[0];
            var y = i[1];
            System.out.print(map[x][y] + "->");
        }
        System.out.println("END");
    }
}

输出结果如下:

fill:dp=5, px=0, py=0
fill:dp=13, px=1, py=1
fill:dp=17, px=2, py=1
fill:dp=18, px=3, py=1
fill:dp=9, px=1, py=1
fill:dp=13, px=2, py=1
fill:dp=18, px=3, py=1
fill:dp=22, px=4, py=1
fill:dp=11, px=1, py=2
fill:dp=18, px=2, py=2
fill:dp=20, px=3, py=2
fill:dp=28, px=4, py=2
fill:dp=13, px=1, py=3
fill:dp=25, px=2, py=3
fill:dp=25, px=3, py=4
fill:dp=31, px=4, py=3
      0   -1000   -1000   -1000   -1000 
  -1000       5       9      11      13 
  -1000      13      13      18      25 
  -1000      17      18      20      25 
  -1000      18      22      28      31 
5->8->4->1->4->6->3->over

Process finished with exit code 0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌白在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值