动态规划【一】到达目标的最大/最小路径

马上面临秋招,笔者也刷过不少题了,但是动态规划一直是笔者心中的痛点,大厂的笔试中动态规划已成标配,为了圆大厂梦,笔者决定沉下心,认真研究一下动态规划问题,近日看到leetcode一位大佬将动态规划问题划分为五类,私以为很受用,加上一些个人见解,将其写成博客,如有什么错误还请各位看官指正。

1、到达目标的最大/最小路径

1.1 问题描述

通常这类问题都具有如下的描述:

给定一个目标,求到达目标所需要的最大/最小代价、最大/最小和或最大/最小路径数

1.2 解题方法

以路径问题为例,通常在所有可能的路径中,最大/最小路径数都由前一个最大/最小路径加上当前的路径数来得出,我们可以列出状态转移方程:

routes[i] = min(route[i-1],routes[i-2],routes[i-3]...routes[i-k])+cost[i]

通常,解决这类问题的代码都是如下形式:

for(int i=1;i<=target;i++){
	for(int j=0;j<ways.size(),j++){
		dp[i]=min(dp[i],dp[i-ways[j]])+cost[i];
	}
}

1.3 leetcode原题解析

以下是笔者搜集的leetcode此类问题,附上笔者的代码,在这里不讨论代码的优化问题。


746. Min Cost Climbing Stairs (Easy)
/**
*爬阶梯问题,类似于青蛙跳台阶,只不过这里加了一个cost,解题方法还是没变
*每一阶的最小代价都由前一个最小代价决定
*/
//解决方案
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int [] dp = new int[cost.length+1];
        dp[0] = cost[0];
        dp[1] = cost[1];
        for (int i = 2;i<=cost.length;i++){
                dp[i]=Math.min(dp[i-1],dp[i-2])+(i==cost.length?0:cost[i]);
            }
        return dp[cost.length];
    }
}
64. Minimum Path Sum (Medium)
/**
*求到达目标点的最小和,先假设只有一行或只有一列,那么只有一条路径
*再考虑其他情况,每次只能往右或往下移
*/
//解决方案
class Solution {
    public int minPathSum(int[][] grid) {
       int row = grid.length;
        int col = grid[0].length;
        for (int i =1;i<row;i++){
            grid[i][0]+=grid[i-1][0];
        }
        for (int j =1;j<col;j++){
            grid[0][j]+=grid[0][j-1];
        }
        for (int i =1;i<row;i++){
            for (int j =1;j<col;j++){
                grid[i][j]=Math.min(grid[i-1][j],grid[i][j-1])+grid[i][j];
            }
        }
        return grid[row-1][col-1]; 
    }
}
322. Coin Change (Medium)
/**
*要求组成amount数量的最少硬币数,要先将0~amount之间的最少硬币数表示出来
*先用一个足够大的数字来填充dp数组(这里我用的10000),如果能换,就选小的替换掉10000
*如果能换,只要将之前的最少硬币数加1就行了,例如10变成15只需要加一个面值为5的硬币,只加了一个硬币
*/
//解决方案
class Solution {
    public int coinChange(int[] coins, int amount) {
        if (amount==0){
            return 0;
        }
        int [] dp = new int[amount+1];
        Arrays.fill(dp, 10000);
        dp[0] = 0;
        for (int j = 1;j<=amount;j++){
            for (int coin : coins) {
                if (coin <= j) {
                    dp[j] = Math.min(dp[j], (dp[j - coin]) + 1);
                }
            }
        }
        return dp[amount]==10000?-1:dp[amount];
    }
}
931. Minimum Falling Path Sum (Medium)
/**
*每一个最小的path都由前一个最小path加上当前path值得到
*当j处于边界时,它的前面状态只有2个,所以需要加判断
*/
//解决方案
class Solution {
    public int minFallingPathSum(int[][] A) {
       int row = A.length;
        int col = A[0].length;
        
        for (int i=1;i<row;i++){
            for (int j =0;j<col;j++){
                if (j==0){
                   A[i][j] = Math.min(A[i-1][j],A[i-1][j+1])+A[i][j];
                }
                else if (j==col-1){
                    A[i][j] = Math.min(A[i-1][j],A[i-1][j-1])+A[i][j];
                }
                else{
                    A[i][j] = Math.min(Math.min(A[i-1][j-1],A[i-1][j]),A[i-1][j+1])+A[i][j];
                }
            }
        }
        Arrays.sort(A[row-1]);
        return A[row-1][0];
    }
}
983. Minimum Cost For Tickets (Medium)
/**
*先利用一个hashset将需要旅行的日子存储起来,当需要旅行时,判断当天的dp最小值
*取出需要旅行的最大日期,就是target,如果某一天不需要旅行,那它的花费就等于前一天的花费,因为不需要花钱
*/
//解决方案
class Solution {
    public int mincostTickets(int[] days, int[] costs) {
        int max_day = days[days.length-1];
        int [] dp = new int[max_day+1];
        dp[0] = 0;
        HashSet<Integer> hashSet = new HashSet<Integer>();
        for (int day : days) {
            hashSet.add(day);
        }
        for (int i =1;i<=max_day;i++){
            if (hashSet.contains(i)){
                int minSevendays = i>=7?i-7:0;
                int minthirtydays = i>=30?i-30:0;
                dp[i] = Math.min(costs[0]+dp[i-1],Math.min(costs[1]+dp[minSevendays],costs[2]+dp[minthirtydays]));
            }
            else {
                dp[i]=dp[i-1];
            }
        }
        return dp[max_day];
    }
}
279. Perfect Squares (Medium)
/**
*和找零钱的思路一样,区别是要先构造出coins
*/
//解决方案
class Solution {
    public int numSquares(int n) {
        int i = 0;
        int [] dp = new int[n+1];
        Arrays.fill(dp,10000);
        dp[0] = 0;
        ArrayList<Integer> arr = new ArrayList<>();
        while (i*i<=n){
            arr.add(i*i);
            i++;
        }
        for (int j=1;j<=n;j++){
            for (int coin:arr){
                if (coin<=j){
                    dp[j] = Math.min(dp[j],dp[j-coin]+1);
                }
            }
        }
        return dp[n];
    }
}
1049. Last Stone Weight II (Medium)
/**
*可以将问题转化成0-1背包问题,尽可能地装背包,使其weight最接近sum/2即可
*注意石头的重量不能超过sum/2
*/
//解决方案
class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for (int stone : stones) {
            sum += stone;
        }

        int [] dp = new int[sum/2+1];
        int maxsize = sum/2;

        for (int curstone : stones) {
            for (int j = maxsize; j >= curstone; j--) {
                dp[j] = Math.max(dp[j], dp[j - curstone] + curstone);
            }

        }
        return sum-2*dp[sum/2]; 
    }
}
120. Triangle (Medium)
/**
*这个问题与931瀑布问题类似,区别是它不是一个标准的矩形,而是有个三角形
*不难发现,当j处于临界值时,它只能来源于dp[i-1][j],当它不是临界值时,它可能来源于
*两个状态:dp[i-1][j-1]和dp[i-1][j],最后根据最后一行的dp[i]排序取最小值
*/
//解决方案
class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        for (int i = 1;i<triangle.size();i++){
            for (int j=0;j<triangle.get(i).size();j++){
                if(j==0){
                    triangle.get(i).set(j,triangle.get(i-1).get(0)+triangle.get(i).get(j));
          
                }
                else if (j==triangle.get(i).size()-1){
                    triangle.get(i).set(j,triangle.get(i-1).get(j-1)+triangle.get(i).get(j));
             
                }
                else {
                    triangle.get(i).set(j,Math.min(triangle.get(i-1).get(j-1),triangle.get(i-1).get(j))+triangle.get(i).get(j));
          
                }

            }

        }
        triangle.get(triangle.size()-1).sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });
        return triangle.get(triangle.size() - 1).get(0);
    }
}
474. Ones and Zeroes (Medium)
/**
*与找零钱问题类似
*这里求的是最大值,当前dp的最大值为当前的最大值和前一状态的最大值+1中的较大值
*/
//解决方案
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
    
    int len=strs.length;
    int dp[][]=new int [m+1][n+1];
    
    for(int i=0; i<len; i++)
    {
        int ones=0,zeros=0;
        for(int j=0; j<strs[i].length(); j++)
        {
            if(strs[i].charAt(j)=='0')
                zeros++;
            else
                ones++;
        }
        
          for(int k=m; k>=zeros; k--)
          {
              for(int l=n; l>=ones; l--)
              {
              dp[k][l]=Math.max(dp[k][l], dp[k-zeros][l-ones]+1);
              }
           }
      }  
    
    return dp[m][n];
}
}
221. Maximal Square (Medium)
/**
*当一个点不为0,它的左边、左上、上边值都不为0时,它们就可以构成一个正方形,正方形的边长为4个值的最小值,以此类推
*char[i][j]-48是为了得到int类型的0和1
*/
//解决方案
class Solution {
    public int maximalSquare(char[][] matrix) {
               if (matrix.length<=1){
            if (matrix.length==0){
                return 0;
            }
            else {
               for(int j =0;j<matrix[0].length;j++){
                   if (matrix[0][j]=='1'){
                       return 1;
                   }
               }
                return 0;      
            }
        }
        int max = 0;
        int [][] tmp_matrix = new int[matrix.length][matrix[0].length];
        for(int i =0 ;i<matrix.length;i++){
            for(int j =0;j<matrix[0].length;j++){
                tmp_matrix[i][j]= matrix[i][j]-48;
            }
        }
        for (int i =1;i<tmp_matrix.length;i++)
        {
            for (int j =1;j<tmp_matrix[0].length;j++){
                if (tmp_matrix[i][j]!=0){
                    if(tmp_matrix[i][j-1]!=0 & tmp_matrix[i-1][j-1]!=0 & tmp_matrix[i-1][j]!=0){
                        tmp_matrix[i][j]=Math.min(Math.min(tmp_matrix[i][j-1],tmp_matrix[i-1][j-1]),tmp_matrix[i-1][j])+1;
                        max = Math.max(tmp_matrix[i][j], max);
                    }
                }
            }
        }
        return max*max;
    }
}

1.4 挑战极限

以下三个题为hard难度,笔者看到难度就已经放弃了(笑哭),但是为了内容的完整性和为大佬铺路,这里还是列出来,感兴趣的同学可以挑战一下。

1240. Tiling a Rectangle with the Fewest Squares (Hard)
174. Dungeon Game (Hard)
871. Minimum Number of Refueling Stops (Hard)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
我了解了您的要求,以下是用YALMIP编写总成本最小路径规划的示例代码: 1. 假设我们有一个包含N个节点的无向图,每个节点都有一个正的运行成本(如电力成本),同时,每个节点之间有一个权重表示通过该节点的边的运行成本。 2. 我们使用一个NxN的矩阵w表示所有节点之间的边的成本,一个向量c表示每个节点的成本。 3. 假设我们从节点1开始,目标到达节点N。 4. 我们需要构建一个约束条件,该约束条件确保经过任何节点的所有出发点和目标都相同,这可以通过加入一个流量平衡约束来实现。 5. 我们使用了一个变量x来表示路径,x(i,j)表示从节点i到节点j的路径是否存在。因此,如果x(i,j)等于1,则意味着从节点i到节点j有一条路径。 6. 我们需要最小化总成本,总成本等于路径成本加上每个节点的成本之和。 现在,我们可以使用以下代码来解决我们的问题: ```matlab %节点数量 N = 10; %产生成本矩阵W和节点成本向量c W = rand(N,N); C = rand(N,1); %创建变量 x = binvar(N,N,'symmetric'); %流量平衡约束条件 for i = 1:N flow_in = sum(x(:,i)); flow_out = sum(x(i,:)); if i == 1 Constraints = [flow_in == 1, flow_out == 0]; elseif i == N Constraints = [Constraints, flow_in == 0, flow_out == 1]; else Constraints = [Constraints, flow_in == flow_out]; end end %计算总成本 total_cost = sum(sum(W.*x)) + sum(C); %最小化总成本 objective = optimize(Constraints, total_cost); %输出最优路径 if objective.problem == 0 opt_path = []; start_node = 1; while start_node ~= N for j = 1:N if abs(value(x(start_node,j))-1) <= 1e-6 opt_path = [opt_path, start_node]; start_node = j; break; end end end opt_path = [opt_path, N]; disp(['最小路径成本:', num2str(value(total_cost))]); disp(['最优路径:', num2str(opt_path)]); else disp(['无解,错误信息为:', yalmiperror(objective.problem)]); end ``` 这段代码可以最小化总成本,并输出从节点1到节点N的最优路径。当然,这只是一个简单的示例,实际应用场景中需要根据具体情况进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值