【动态规划】三角矩阵


三角矩阵之类的题主要就是求最小距离,之后遇见有关于矩阵、网格、字符串间的比较问题,用一维动归难以解决或者难以理解的情况下,可以考虑着用二维动归,接下来就用三角矩阵案例来介绍一下动归问题的各种思想。

牛客链接

题目:给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,例如,给出的三角形如下:

[[20],[30,40],[60,50,70],[40,10,80,30]]

最小的从顶部到底部的路径和是20 + 30 + 50 + 10 = 110。

自顶向下

在这里插入图片描述

分析思路:

想要用自顶向下的方法求最短路径之和就需要求从坐标(0, 0)到(i,j)的最小路径之和,最后在三角矩阵的最后一行必然都是到达该点的最短路径和,只需要遍历最后一行,找到其中最小的路径之和就解决了问题

每一个元素都有两种移动路径,坐标(i,j)可以移动到坐标(i+1,j)和(i+1,j+1)处

移动到坐标(i,j)处有出现三种可能

  1. 坐标中的列坐标为0时(i,0),移动到该点的坐标只可能是坐标(i-1, 0)
  2. 坐标中行坐标和列坐标相同时(例如(1, 1)之类的),移动到该坐标的只可能是坐标(i-1,j-1)
  3. 其他情况下,移动到该坐标都有两个坐标(i-1,j-1)和(i-1,j)

那么在求最短路径和时只需要将该坐标的值加上到达该坐标前的最短路径和

将状态设置为F(i,j) :从(0,0)坐标到(i,j)坐标的最短路径和

例如

F(0,0)= 20;

F(1,0)= F(0,0)+ 30 = 50 ;F(1,1)= F(0,0)+ 40 = 60;

F(2,1) = min(F(1,0),F(1,1))+ 50 = 100;

综上所述:

  1. 状态定义F(i,j):从(0,0)坐标到(i,j)坐标的最短路径和
  2. 状态间的转移方程定义F(i,j):
  • min(F(i-1,j-1),F(i-1,j)) + path[i][j] (0 < j < i)
  • F(i-1,0) + path[i][0] (j == 0)
  • F(i-1,j-1) + path[i][j] (i == j)
  1. 状态的初始化F(0,0) = path[0][0]
  2. 返回结果min(F(row-1,j))
import java.util.*;
public class Solution {
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        if(triangle==null||triangle.isEmpty()) return 0;//若出现传入的线性表为空或没有元素的情况,返回0
        int row = triangle.size();//triangle的大小就是三角矩阵的行
        int[][] path = new int[row][row];//定义path二维数组来存放三角矩阵中每个坐标的状态即最短路径和
        path[0][0] = triangle.get(0).get(0);//状态的初始化
        for(int i = 1;i < row;i ++) {
            for(int j = 0;j <= i;j ++) {
                if(j == 0) {
                    path[i][j] = path[i-1][j] + triangle.get(i).get(j);//转移方程的情况二
                } else if(i == j) {
                    path[i][j] = path[i-1][j-1] + triangle.get(i).get(j);//转移方程的情况三
                }else {
                    path[i][j] = Math.min(path[i-1][j],path[i-1][j-1]) + triangle.get(i).get(j);
                    //转移方程的情况一
                }
            }
        } 
        int min = path[row-1][0];
        //将三角矩阵状态最后一行的第一个元素当为最小路径和,通过遍历最后一行找到最小的路径和
        for(int j = 1;j < row;j ++) {
            if(path[row-1][j] < min) 
                min = path[row-1][j]; 
        }
        return min;//返回最小路径和
    }
}

自底向上

事实上,比起自顶向下,自底向上的方法会更加简单,也更方便理解

分析思路:

自底向上的思路,设置的状态是从坐标(i,j)到最后一行的最短距离,这就需要三角矩阵的最后一行不变(初始化条件)。另外,三角矩阵的每个元素(除了最后一行)都有两种移动路径,就可以避免了上一种思路中转移方程需要考虑各种情况的麻烦,最后坐标(0,0)处的数值就是三角矩阵最短路径和。

例如

F(3,0)= 40;F(3,1)=10;

F(2,0) = min(F(3,0),F(3,1))+ 60 = 70;

综上所述:

  1. 状态定义F(i,j):从(i,j)坐标到最后一行的最短路径和

  2. 状态间的转移方程定义F(i,j):min(F(i+1,j),F(i+1,j+1)) + path[i][j] (0 <= j <= i)

  3. 状态的初始化F(row-1,0) = path[row-1][j]

  4. 返回结果F(0,0)

import java.util.*;
public class Solution {
    public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
        if(triangle==null||triangle.isEmpty()) return 0;//若出现传入的线性表为空或没有元素的情况,返回0
        int row = triangle.size();//triangle的大小就是三角矩阵的行
        int[][] path = new int[row][row];//定义path二维数组来存放三角矩阵中每个坐标的状态即最短路径和
        //状态最后一行的值为三角矩阵最后一行原来的值
        for(int j = 0;j < row;j++) {
            path[row -1][j] = triangle.get(row -1).get(j); //状态的初始化
        }
        //从倒数第二行开始
        for(int i = row-2;i >= 0;i--) {
            for(int j = 0;j <= i;j++) {
                path[i][j] = Math.min(path[i+1][j],path[i+1][j+1]) + triangle.get(i).get(j);//状态转移
            }
        }
        return path[0][0];//返回最后结果
    }
}

O(N)空间

事实上,可以只用O(N)的额外的空间来完成这项工作,其中N是三角形中的行总数。

主要的思路和自底向上的思路是差不多的,只是存储的方式出现了差别,不再将每个坐标的状态单独放在数组里。这里选择用大小为三角矩阵的行数的一维数组来存状态。

在这里插入图片描述

import java.util.*;
public class Solution {
   public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
       if(triangle==null||triangle.size()==0){
           return 0;
       }
       int row=triangle.size();
       int[]path=new int[row];
       //初始化状态
       for(int j=0;j<row;j++){
           path[j]=triangle.get(row-1).get(j);
       }
       for(int i=row-2;i>=0;i--){
           for(int j=0;j<=i;j++){
               path[j]=triangle.get(i).get(j)+Math.min(path[j],path[j+1]);//状态转移
           }
       }
       return path[0];//返回结果
   }
}

完!

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

富春山居_ZYY(已黑化)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值