力扣刷题笔记19——礼物的最大价值/动态规划/2种递归,1种非递归的回溯

礼物的最大价值/动态规划/2种递归,1种非递归的回溯

问题

来自力扣

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
 
示例 1:
输入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 12
解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物

提示:
0 < grid.length <= 200
0 < grid[0].length <= 200

我的代码1

//方法1
	 int maxValue(vector<vector<int>>& grid) {
		 if (grid.size() == 0) return 0;
		 if (grid.size() == 1&& grid[0].size() == 1) return grid[0][0];

		 int hor_n = 0, ver_n = 0;

		 //横向
		 if (grid[0].size() > 1) {
			 vector<vector<int>> tmp;
			 for (int i = 0; i < grid.size(); ++i) {
				 //vector<int> tmptmp(grid[i].begin(), grid[i].end() - 1);
				 tmp.push_back(vector<int> (grid[i].begin(), grid[i].end() - 1));
			 }
			 hor_n = maxValue(tmp);

		 }

		 //纵向
		 if (grid.size() > 1) {
			 vector<vector<int>> tmp(grid.begin(), grid.end() - 1);
			 ver_n = maxValue(tmp);
		 }

		 return max(hor_n, ver_n)+ grid[grid.size()-1][grid[0].size() - 1];
	 }

思路:我如果想要得到最多的礼物。那么我先看右下角的那一格(m,n),即我是从(m,n-1)向右走到(m,n)还是从(m-1,n)向下走到(m,n)。
方法1:所以可以用递归函数V,求出V(m,n-1)和V(m-1,n),然后选择较大的那个,最后加上(m,n)自身的值,就得到了V(m,n)。
缺点:我每次传递给递归函数的二维矩阵,都需要重新定义,这样不仅占用存储空间,而且复杂度很大。导致方法可行,但是当grid很大时,算不出来。

我的代码2

方法2
	 int maxValue(vector<vector<int>>& grid) {
		 if (grid.size() == 0) return 0;
		 if (grid.size() == 1 && grid[0].size() == 1) return grid[0][0];

		 int hor_n = 0, ver_n = 0;
		 //横向
		 if (grid[0].size() > 1) {
			 hor_n = maxvalue(grid, 0, 1);
		 }
		 //纵向
		 if (grid.size() > 1) {
			 ver_n = maxvalue(grid, 1, 0);
		 }
		 return max(hor_n, ver_n) + grid[grid.size() - 1][grid[0].size() - 1];
	 }
	 //递归函数
	 int maxvalue(vector<vector<int>>& grid, int m, int n) {
		 if (grid.size()- m == 1 && grid[0].size() - n == 1) return grid[0][0];
		 //如果只有1行,但有多列
		 if (grid.size() - m == 1) {
			 //return maxvalue(grid, m, n + 1) + grid[grid.size() - 1 - m][grid[0].size() - 1 - n];
			 return accumulate(grid[0].begin(), grid[0].end() - n, 0);

		 }
		 //如果只有1列,但有多行
		 if (grid[0].size() - n == 1) {
			 //return maxvalue(grid, m + 1, n) + grid[grid.size() - 1 - m][grid[0].size() - 1 - n];
			 int acc=0;
			 for (int i = 0; i < grid.size() - m;++i) {
				 acc += grid[i][0];
			 }
			 return acc;
		 } 

		 return max(maxvalue(grid, m, n + 1), maxvalue(grid, m + 1, n)) + grid[grid.size() - 1 - m][grid[0].size() - 1 - n];
	 }

方法2:方法2的思路和方法1基本一样,只是我为了不去频繁定义数组,将递归的内容单独写成一个函数,然后多传递2个参数(说明目前是有几行几列)。
缺点:方法2虽然比方法1好一些,但是当数组更大时,还是计算不出来。因为求V(m,n)的时候,我算V(m,n-1)时,需要算出所有的V(i,j),i<m,j<n-1。而算V(m-1,n)时,需要算出所有的V(i,j),i<m-1,j<n。很明显,V(i,j),i<m-1,j<n-1.都被重复算了2遍。同样的,算每个V(i,j)时,也会重复算2遍V(i-k,j-k),k<i or j。

我的代码3

class Solution {
 public:
	 int maxValue(vector<vector<int>>& grid) {
		 if (grid.size() == 0) return 0;
		//  if (grid.size() == 1 && grid[0].size() == 1) return grid[0][0];
		 //vector<vector<int>> gvalue(grid);
		 vector<vector<int>> gvalue(grid.size(), vector<int>(grid[0].size()));
		 for (int i = 0; i < grid.size(); ++i) {
			 for (int j = 0; j < grid[0].size(); ++j) {
				 int vtop, vleft;
				 if (i == 0) 
					 vtop = 0;
				 else 
					 vtop = gvalue[i - 1][j];
				 if (j == 0)
					 vleft = 0;
				 else
					 vleft = gvalue[i][j - 1];
				 gvalue[i][j]=max(vtop, vleft)+grid[i][j];
			 }

		 }
		 return gvalue[grid.size() - 1][grid[0].size() - 1];
	 }
 };
 int main() {
	vector<vector<int>> grid;
	vector<vector<int>> grid2;
	vector<int> tmp;
	tmp = { 1,3,1};
	grid.push_back(tmp);
	tmp = { 1,5,1 };
	grid.push_back(tmp);
	tmp = { 4,2,1 };
	grid.push_back(tmp);
	cout << grid.size() << "  " << grid[0].size()<<endl;
	Solution mysolution;
	int s = mysolution.maxValue(grid);
	int s2 = mysolution.maxValue(grid2);
	cout << s<<"\t"<<s2;
	return 0;
}

我之前思路上没错,只是陷入了一个误区:思路和思路的实现是不一定要一样的。
什么意思呢?
求V(m,n)的时候,需要求出V(m,n-1)和V(m-1,n),但我不应该最先就开始求V(m,n-1)和V(m-1,n),而是先求出比它们还靠前的所有V。所以出现了方法3。
方法3:先从V[0][0]开始求,这样就可以避免重复计算。

示例代码

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
         vector<vector<int>>vec(grid.size(),vector<int>(grid[0].size(),0));
         
         for(size_t i=0;i<vec[0].size();++i){
             if(i==0)vec[0][i]=grid[0][0];
             else vec[0][i]=vec[0][i-1]+grid[0][i];
         }

         for(size_t i=0;i<vec.size();++i){
             if(i==0)vec[i][0]=grid[0][0];
             else vec[i][0]=vec[i-1][0]+grid[i][0];
         }

         for(size_t i=1;i<vec.size();++i){
             for(size_t j=1;j<vec[0].size();++j){
                 vec[i][j]=max(vec[i-1][j]+grid[i][j],vec[i][j-1]+grid[i][j]);
             }
         }
         return vec[grid.size()-1][grid[0].size()-1];

    }
};

示例代码的方法和方法3基本一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小欣CZX

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

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

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

打赏作者

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

抵扣说明:

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

余额充值