暴力递归和动态规划解决二维数组最小路径和的问题(C++实现)

暴力递归:
1, 把问题转化为规模缩小了的同类问题的子问题
2, 有明确的不需要继续进行递归的条件(base case)
3, 有当得到了子问题的结果之后的决策过程
4, 不记录每一个子问题的解

动态规划:
1, 从暴力递归中来
2, 将每一个子问题的解记录下来, 避免重复计算
3, 把暴力递归的过程, 抽象成了状态表达
4, 并且存在化简状态表达, 使其更加简洁的可能

#include<iostream>
#include<vector>
using namespace std;

class Solution {
public:
     int minPathSum(vector<vector<int> > &grid,int i,int j) {//暴力递归实现,参数i,j表示从点(i,j)到右下角点的最短路径
    		if (grid.empty())
		    	return 0;
		int m = grid.size();
		int n = grid[0].size();
		if (i == m - 1 && j == n - 1)//点(i,j)如果就是右下角点,那么直接返回
		        return grid[i][j];
	    	if (i == m - 1)
	    		return grid[i][j] + minPathSum2(grid, i, j + 1);//最后一行是basecase
	    	if(j==n-1)
	        	return grid[i][j]+ minPathSum2(grid, i+1, j);//最后一列也是basecase
	    	int right= minPathSum2(grid, i, j + 1);
	    	int down = minPathSum2(grid, i + 1, j);
	    	return (right < down ? right : down) + grid[i][j];
        //从(i,j)点到右下角点的最短路径等于(i,j)点的值和(i+1,j)点、(i,j+1)点到右下角点最短路径较小的相加
		
	}
	int minPathSum(vector<vector<int> > &grid) {//动态规划实现
			if (grid.empty())
				return 0;
			int m = grid.size();
			int n = grid[0].size();
			vector<vector<int>> dp(m, vector<int>(n, grid[0][0]));
			for (int i = 1; i<m; i++) {
				dp[i][0] = dp[i - 1][0] + grid[i][0];//第一列的值是可以确定的,是basecase
			}
			for (int i = 1; i<n; i++) {
				dp[0][i] = dp[0][i - 1] + grid[0][i];//第一行的值也是可以确定的,是basecase
			}
			for (int i = 1; i<m; i++) {
				for (int j = 1; j<n; j++) {
					dp[i][j] = (dp[i - 1][j]<dp[i][j - 1]? dp[i - 1][j]: dp[i][j - 1]) + grid[i][j];
					//到(i,j)点的最短路径等于到(i-1,j)点和到(i,j-1)点的最短路径短的那个再加上(i,j)点的值
				}
			}
			return dp[m - 1][n - 1];
		}
	
};
int main()
{
	vector<vector<int>> v(4, vector<int>(3));//这块是4行3列二维vector的初始化,用二维vector比二维数组要简单些
	for (int i = 0; i<4; i++)
		for (int j = 0; j<3; j++)  
			v[i][j] = i+j;

	Solution s;
	cout << s.minPathSum(v) << endl;
	cout << s.minPathSum2(v,0,0) << endl;
	system("pause");
	return 0;
}

从上面图片可以看出,暴力递归会重复计算,浪费时间,而动态规划使用了一个二维数组dp来保存已经求出来的那些点最短路径,动态规划算法是 “记住求过的解来节省时间”,用空间换时间,它的核心就是记住已经解决过的子问题的解

然后对于为什么使用二维数组保存已经求出的解,为什么不是一维呢?我们分析可变参数,可变参数是二维vector的横纵坐标i和j,可变参数有几个就用几维的表来存,二维vector的长宽的范围就是两个可变参数的范围,每个位置表示(0,0)到此位置的最短路径,所有的返回值一定可以装在这张二维表里面,把二维表的basecase填好(也就是第一行和第一列 ),二维表(m-1,n-1)的位置就是我要求的结果,看其中求普通位置需要哪些其他位置的值,逆着回去就是我填表的顺序。

参考:https://blog.csdn.net/weixin_38628152/article/details/80275588https://blog.csdn.net/u014303647/article/details/80335627https://blog.csdn.net/weixin_37766296/article/details/80113760

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 递归求解二维数组路径可以使用暴力递归的方式。思路是,对于二维数组的每一个点,记录其到终点(右下角点)的最小路径和。具体做法是,分别查看该点的正下方和右边两个点的值,选择其较小的一个与数组原位置的值相加。最终,数组左上角位置的值即为问题的解。\[1\] #### 引用[.reference_title] - *1* [二维数组从左上到右下,返回最小路径问题的解法](https://blog.csdn.net/z1171127310/article/details/127716640)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [LeetCode题目总结——二维数组](https://blog.csdn.net/weixin_40131652/article/details/108680706)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [二维数组最小路径问题](https://blog.csdn.net/hotonyhui/article/details/126962888)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值