剑指 Offer 42. 连续子数组的最大和
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans = nums[0], cur = nums[0], n = nums.size();
for(int i = 1; i < n; ++ i){
if(cur < 0)cur = 0;
cur += nums[i];
ans = max(ans, cur);
}
return ans;
}
};
算法思路:
假设和最大的子串a
由b
和c
组成,b
是前面部分,c
是后面部分,则b
子串的和必须大于0
,否则c
串的和会比a
串和大,与结果矛盾,因此我们可以从第一个元素开始往后计算和,当和小于0
时说明该子串不能成为其它串的前缀,和重置为0
,继续往后遍历。定一个长度为n的数组dp
,dp[i]
表示以nums[i]
结尾的子串的最大值,最后取dp
的最大值就是答案。又因为dp[i]
的值相互独立,我们可以在遍历时就开始比较,定义ans
表最大值,减少空间开销。
剑指 Offer 47. 礼物的最大价值
在一个 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
解法一:
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
queue<pair<int, int>> q;
q.push(pair<int, int>(0, 0));
int ans = -INT_MAX, n = grid.size(), m = grid[0].size();
int dirs[2][2] = {{0, 1}, {1, 0}}, pred[2][2] = {{-1, 0}, {0, -1}};
vector<vector<bool>> vis(n, vector<bool>(m));
vis[0][0] = true;
while(q.empty() == false){
auto p = q.front();
q.pop();
int x = p.first, y = p.second, best = grid[x][y];
ans = max(ans, best);
for(int i = 0; i < 2; ++ i){
int ox = x + pred[i][0], oy = y + pred[i][1];
int nx = x + dirs[i][0], ny = y + dirs[i][1];
if(ox >= 0 && ox < n && oy >= 0 && oy < m){
best = max(best, grid[x][y] + grid[ox][oy]);
}
if(nx >= 0 && nx < n && ny >= 0 && ny < m){
if(vis[nx][ny] == false){
q.push(pair<int, int>(nx, ny));
vis[nx][ny] = true;
}
}
}
grid[x][y] = best;
}
return grid[n - 1][m - 1];
}
};
算法思路:
题目表示每次只能向右和向下走,可以把整个棋盘看成一张图,就如例子那样
[1 3 1] [ 1 ]
[1 5 1] => [ 1 3 ]
[4 2 1] [ 4 5 1 ]
[ 2 1 ]
[ 1 ]
从菱形的最上层开始遍历,一直往下走,每个结点的值就是上面一或两个结点的最大值加上本身,最底层结点的值就是最大路径,相当于暴力广度优先搜索,期间还需要去重。
解法二:
class Solution {
public:
int maxValue(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
for(int i = 0; i < n; ++ i){
for(int j = 0; j < m; ++ j){
if(i != 0 || j != 0){
if(i == 0){
grid[i][j] = grid[i][j] + grid[i][j - 1];
}else if(j == 0){
grid[i][j] = grid[i][j] + grid[i - 1][j];
}else grid[i][j] = max(grid[i][j - 1], grid[i - 1][j]) + grid[i][j];
}
}
}
return grid[n - 1][m - 1];
}
};
算法思路:
由思路一可看到,这道题最重要的就是一个结点的值是依靠其上面两个结点的值,这两个结点说白了就是上边和左边两个结点,因此我们不需进行广度优先搜索,只需要从最左上角的结点开始,从左到右从上往下遍历,这样就使遍历到的结点的上边和左边结点先遍历到,可以直接得到结果,矩阵的最右下角的值就是答案。
【剑指Offer】系列:
【剑指Offer】栈
【剑指Offer】链表
【剑指Offer】字符串
【剑指Offer】查找算法
【剑指Offer】查找算法(1)
【剑指Offer】搜索与回溯算法
【剑指Offer】查找算法(1)
【剑指Offer】搜索与回溯算法
【剑指Offer】搜索与回溯算法(1)
【剑指Offer】动态规划