day60 2023/04/01
一、柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
分析如下:
本地单调栈的解法和接雨水的题目是遥相呼应的。
为什么这么说呢,接雨水那道题目找每个柱子左右两边第一个大于该柱子高度的柱子,而本题是找每个柱子左右两边第一个小于该柱子的柱子。
这里就涉及到了单调栈很重要的性质,就是单调栈里的顺序,是从小到大还是从大到小。
单调栈从栈头(元素从栈头弹出)到栈底的顺序应该是从小到大的顺序。
那么因为本题是要找每个柱子左右两边第一个小于该柱子的柱子,所以从栈头(元素从栈头弹出)到栈底的顺序应该是从大到小的顺序!
举一个例子,如图:
只有栈里从大到小的顺序,才能保证栈顶元素找到左右两边第一个小于栈顶元素的柱子。
所以本题单调栈的顺序正好与接雨水反过来。
此时大家应该可以发现其实就是栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度
理解这一点,对单调栈就掌握的比较到位了。
除了栈内元素顺序和接雨水不同,剩下的逻辑就都差不多了。
主要就是分析清楚如下三种情况:
代码如下:
class Solution {
public:
int largestRectangleArea(vector& heights) {
int result = 0;
stack st;
heights.insert(heights.begin(), 0); // 数组头部加入元素0
heights.push_back(0); // 数组尾部加入元素0
st.push(0);
// 第一个元素已经入栈,从下标1开始
for (int i = 1; i < heights.size(); i++) {
if (heights[i] > heights[st.top()]) { // 情况一
st.push(i);
} else if (heights[i] == heights[st.top()]) { // 情况二
st.pop(); // 这个可以加,可以不加,效果一样,思路不同
st.push(i);
} else { // 情况三
while (!st.empty() && heights[i] < heights[st.top()]) { // 注意是while
int mid = st.top();
st.pop();
if (!st.empty()) {
int left = st.top();
int right = i;
int w = right - left - 1;
int h = heights[mid];
result = max(result, w * h);
}
}
st.push(i);
}
}
return result;
}
};
复习复习
股票买卖那道题可以使用贪心来做(将股票收益离散化,每天都获得正利润即可保证整体利润最大)或者采用动态规划来做
动态规划代码如下:
//股票买卖问题动态规划做法
#include
using namespace std;
vector prices;
int main()
{
int x;
while(scanf("%d",&x)!=EOF) prices.push_back(x);
vector< vector > dp(prices.size(),vector(2,0));
//初始化dp数组
dp[0][0]=-1*prices[0];
//第一天持有股票获得的最大利润
for(int i=1;i
跳跃游戏
跳几步无所谓,关键在于跳的覆盖范围
//跳跃问题
#include
using namespace std;
vector nums;
int main()
{
int x;
while(scanf("%d",&x)!=EOF) nums.push_back(x);
int cover=0;
for(int i=0;i<=cover;i++)
{
cover=max(i+nums[i],cover);
if(cover>=nums.size()-1)
{
printf("yes\n");
return 0;
}
}
printf("no\n");
return 0;
}
跳跃游戏Ⅱ
这题使用动态规划更好做
//跳跃问题Ⅱ:dp做法
#include
using namespace std;
vector nums;
int main()
{
int x;
while(scanf("%d",&x)!=EOF) nums.push_back(x);
vector dp(nums.size(),INT_MAX);
dp[0]=0;
for(int i=1;i=i)
dp[i]=min(dp[i],dp[j]+1);
}
printf("%d\n",dp[nums.size()-1]);
return 0;
}
k次取反后最大化的数组和
代码如下:
//k次取反后最大化的数组和
#include
using namespace std;
vector nums;
int k;
bool cmp(int a,int b)
{
return abs(a)>abs(b);
}
int main()
{
scanf("%d",&k);
int x;
while(scanf("%d",&x)!=EOF) nums.push_back(x);
sort(nums.begin(),nums.end(),cmp);
for(int i=0;i0)
{
nums[i]*=-1;
k--;
}
}
if(k%2==1) nums[nums.size()-1]*=-1;
int res=0;
for(int i=0;i
加油站
代码如下:贪心思路难想
//加油站
#include
using namespace std;
vector gas;
vector cost;
int main()
{
int x,y;
while(scanf("%d",&x)!=EOF) gas.push_back(x);
while(scanf("%d",&y)!=EOF) cost.push_back(y);
int totalSum=0,curSum=0,start=0;
for(int i=0;i
分发糖果
两次贪心策略
//分发糖果
//两头贪心
#include
using namespace std;
vector nums;
int main()
{
int x;
while(scanf("%d",&x)!=EOF) nums.push_back(x);
vector res(nums.size(),1);
//初始化res数组
//从前往后
for(int i=1;inums[i-1])
res[i]=res[i-1]+1;
}
//从后往前
for(int i=res.size()-2;i>=0;i--)
{
if(nums[i]>nums[i+1])
res[i]=max(res[i],res[i+1]+1);
}
//统计结果
int sum=0;
for(int i=0;i
柠檬水找零————思路简单捏
//柠檬水找零
#include
using namespace std;
vector bills;
bool check(vector &bills)
{
int five=0,ten=0,twenty=0;
for(int i=0;i0&&ten>0)
{
five--;
ten--;
twenty++;
}
else if(five>=3)
{
five-=3;
twenty++;
}
else return false;
}
}
return true;
}
int main()
{
int x;
while(scanf("%d",&x)!=EOF) bills.push_back(x);
if(check(bills)) printf("yes\n");
else printf("no\n");
return 0;
}