一、跳跃游戏Ⅱ
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。假设你总是可以到达数组的最后一个位置。
提示:
- 1 <= nums.length <= 10^4
- 0 <= nums[i] <= 1000
示例 1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
- 思路分析+代码分析
我们需要尽可能少的跳跃,那么则需要尽可能大的跳跃,如果一个起跳点的跳跃格子为3,那么后面3个格子都可以作为下一个起跳点,我们需要选择这三个格子中跳的最远的格子(贪心)
#include<iostream>
#include<vector>
using namespace std;
int jump(vector<int>& nums);
int main(){
int n;
cout<<"输入数组个数:";
cin>>n;
vector<int >nums(n);
cout<<"输入跳跃的步数:";
for(int i=0;i<n;i++){
cin>>nums[i];
}
cout<<"需要"<<jump(nums)<<"步";
return 0;
}
int jump(vector<int>& nums) {
int n=nums.size();
int ans=0;
int start=0,end=1;
while(end<n){
int maxPos=0;
//找到最大
for(int i=start;i<end;i++){
maxPos=max(maxPos,nums[i]+i);
}
//下一次起跳开始是这一次起跳的结束
start=end;
//下一次起跳结束是这一次能跳到最大的范围
end=maxPos+1;
//步数+1
ans++;
}
return ans;
}
找离终点最远的格子(即从头开始遍历,首先能到达终点的格子)
我们知道最终要到达最后一个位置,然后我们找前一个位置,遍历数组,找到能到达它的位置,离它最远的就是要找的位置。然后继续找上上个位置,最后到了第 0 个位置就结束了。
至于离它最远的位置,其实我们从左到右遍历数组,第一个满足的位置就是我们要找的。
#include<iostream>
#include<vector>
using namespace std;
int jump(vector<int>& nums);
int main(){
int n;
cout<<"输入数组个数:";
cin>>n;
vector<int >nums(n);
cout<<"输入跳跃的步数:";
for(int i=0;i<n;i++){
cin>>nums[i];
}
cout<<"需要"<<jump(nums)<<"步";
return 0;
}
int jump(vector<int>& nums) {
int n=nums.size();
int ans=0;
int pos=n-1;
while(pos!=0){
//找到能到达pos的最小下标
for(int i=0;i<pos;i++){
if(nums[i]+i>=pos){
pos=i;
ans++;
break;
}
}
}
return ans;
}
二、接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
提示:
- n == height.length
- 1 <= n <= 2 * 10^4
- 0 <= height[i] <= 10^5
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
整个思路就是,求第 i 层的水,遍历每个位置,如果当前的高度小于 i,并且两边有高度大于等于 i 的,说明这个地方一定有水,水就可以加 1。
如果求高度为 i 的水,首先用一个变量 temp 保存当前累积的水,初始化为 0。从左到右遍历墙的高度,遇到高度大于等于 i 的时候,开始更新 temp。更新原则是遇到高度小于 i 的就把 temp 加 1,遇到高度大于等于 i 的,就把 temp 加到最终的答案 ans 里,并且 temp 置零,然后继续循环。
具体看代码:
int trap(vector<int>& height) {
int ans=0;
int maxH=INT_MIN;
//找到最大高度
for(int i=0;i<height.size();i++){
maxH=max(maxH,height[i]);
}
//只要小于,数组之前的都要加1
vector<vector<int>>h(maxH+1);
for(int i=0;i<height.size();i++){
int temp=height[i];
for(int j=1;j<=temp;j++)h[j].push_back(i);
}
//一行一行来计算
for(int i=1;i<=maxH;i++){
int m=h[i].size();
int left=h[i][0];
int right=h[i][m-1];
ans+=right-left+1-m;
}
return ans;
}
求每一列的水,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。
装水的多少,当然根据木桶效应,我们只需要看左边最高的墙和右边最高的墙中较矮的一个就够了。
按列计算+动态规划
解法二中。对于每一列,我们求它左边最高的墙和右边最高的墙,都是重新遍历一遍所有高度,这里我们可以优化一下。
首先用两个数组,max_left [i] 代表第 i 列左边最高的墙的高度,max_right[i] 代表第 i 列右边最高的墙的高度。(一定要注意下,第 i 列左(右)边最高的墙,是不包括自身的,和 leetcode 上边的讲的有些不同)
对于 max_left我们其实可以这样求。
max_left [i] = Max(max_left [i-1],height[i-1])。它前边的墙的左边的最高高度和它前边的墙的高度选一个较大的,就是当前列左边最高的墙了。
对于 max_right我们可以这样求。
max_right[i] = Max(max_right[i+1],height[i+1]) 。它后边的墙的右边的最高高度和它后边的墙的高度选一个较大的,就是当前列右边最高的墙了。
int trap(vector<int>& height) {
int ans=0;
int n=height.size();
//保留左边最大
vector<int>left(n);
//保留右边最大
vector<int>right(n) ;
for(int i=1;i<n-1;i++){
left[i]=max(left[i-1],height[i-1]);
}
for(int i=n-2;i>=0;i--){
right[i]=max(right[i+1],height[i+1]);
}
//计算接水量
for(int i=1;i<height.size();i++){
int res=min(left[i],right[i]);
ans+=(res-height[i])>0?res-height[i]:0;
}
return ans;
}
- #include<iostream>
- #include<vector>
- using namespace std;
- int trap(vector<int>& height);
- int main(){
- int n;
- cout<<"柱子个数:";
- cin>>n;
- vector<int>height(n);
- cout<<"输入柱子高度:";
- for(int i=0;i<n;i++){
- cin>>height[i];
- }
- cout<<"能接到的雨水为:"<<trap(height);
- return 0;
- }
- int trap(vector<int>& height) {
- int ans=0;
- int maxH=INT_MIN;
- //找到最大高度
- for(int i=0;i<height.size();i++){
- maxH=max(maxH,height[i]);
- }
- //只要小于,数组之前的都要加1
- vector<vector<int>>h(maxH+1);
- for(int i=0;i<height.size();i++){
- int temp=height[i];
- for(int j=1;j<=temp;j++)h[j].push_back(i);
- }
- //一行一行来计算
- for(int i=1;i<=maxH;i++){
- int m=h[i].size();
- int left=h[i][0];
- int right=h[i][m-1];
- ans+=right-left+1-m;
- }
- return ans;
- }
- #include<iostream>
- #include<vector>
- using namespace std;
- int trap(vector<int>& height);
- int main(){
- int n;
- cout<<"柱子个数:";
- cin>>n;
- vector<int>height(n);
- cout<<"输入柱子高度:";
- for(int i=0;i<n;i++){
- cin>>height[i];
- }
- cout<<"能接到的雨水为:"<<trap(height);
- return 0;
- }
- int trap(vector<int>& height) {
- int ans=0;
- for(int i=1;i<height.size();i++){
- int left=0;
- //找左边的最大值
- for(int l=i-1;l>=0;l--){
- left=max(left,height[l]);
- }
- int right=0;
- //找右边的最大值
- for(int r=i+1;r<height.size();r++){
- right=max(right,height[r]);
- }
- int res=min(left,right);
- if(res>height[i])ans+=res-height[i];
- }
- return ans;
- }
- 按列计算+动态规划
- #include<iostream>
- #include<vector>
- using namespace std;
- int trap(vector<int>& height);
- int main(){
- int n;
- cout<<"柱子个数:";
- cin>>n;
- vector<int>height(n);
- cout<<"输入柱子高度:";
- for(int i=0;i<n;i++){
- cin>>height[i];
- }
- cout<<"能接到的雨水为:"<<trap(height);
- return 0;
- }
- int trap(vector<int>& height) {
- int ans=0;
- int n=height.size();
- //保留左边最大
- vector<int>left(n);
- //保留右边最大
- vector<int>right(n) ;
- for(int i=1;i<n-1;i++){
- left[i]=max(left[i-1],height[i-1]);
- }
- for(int i=n-2;i>=0;i--){
- right[i]=max(right[i+1],height[i+1]);
- }
- //计算接水量
- for(int i=1;i<height.size();i++){
- int res=min(left[i],right[i]);
- ans+=(res-height[i])>0?res-height[i]:0;
- }
- return ans;
- }