问题
来自力扣:
在一个 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基本一样。