题目:小C的记事本
解析:
本题就在进行1,2操作之前将字符串记录到栈中,当遇到4操作时将栈顶字符串出栈作为新的操作字符。同时2的删除末尾几个字符的操作,需要使用的函数需要记忆一下,reverse(a,b)翻转该字符串,erase(a,b)删除索引a到b
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int q;
while(cin>>q){
string a;
stack<string >st;
while(q--){
int x;
cin>>x;
if(x==1){
string s;
st.push(a);
cin>>s;
a+=s;
}
else if(x==2){
st.push(a);
int k;
cin>>k;
reverse(a.begin(),a.end());//翻转字符串函数
a.erase(0,k);
reverse(a.begin(),a.end());
//a=a.substr(0,len-k);
}
else if(x==3){
int k;
cin>>k;
k--;
cout<<a[k]<<endl;
}
else{
a=st.top();
st.pop();
}
}
}
return 0;
}
题目:区区区间间间
解析:
本题需要求出每一个区间最大值最小值相减的和,可以变为求所有区间的最大值相加,再减去所有区间的最小值,再继续思考,如何求出每个区间的最小值;又可以转变为求出ai这个元素对最大值最小值的贡献;我们举例求每个值对最大值的贡献,他的贡献就在他左边第一个比他大的数,以及右边第一个比他大的值之间的区间有贡献,他对最大值贡献就区间就为,该值作为端点的区间(r[i]-l[i]-2),和该值作为中间点点区间(r[i]-i-1)(i-l[i]-1)。
那么如何求他左/右边比他大的值的下标呢?比如求左比大的值第一次出现的坐标,可以用单调栈,每次到一个数时就与当前栈顶进行比较直到出现比他的元素时,(小于等于的就出栈)他的左下标就是栈顶,并将当前元素下标入栈。
右边最大值的下标就一样判断,从右往左,但是需要注意的一点就是判断右边的时候只有该判定元素大于栈顶才将栈顶踢出,等于的就不踢出了,因为之前判断左值时取了等于的元素区间,现在再取,会有元素取相同的区间;
最小值只需要将所有的元素取负值,小的就会变成大值,大值变为小值原理和求最大值一致。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
int a[100005];
int l[100005];
int r[100005];
int n;
int solve(){
stack<int> t1;
stack<int >t2;
for(int i=1;i<=n;i++){
while(!t1.empty()&&a[i]>=a[t1.top()]) {t1.pop();}
if(t1.empty()){
l[i]=0;//没有比他大的取0
t1.push(i);
}
else l[i]=t1.top(),t1.push(i);
}
for(int i=n;i>0;i--){
while(!t2.empty()&&a[i]>a[t2.top()]){t2.pop();}
if(t2.empty()){
r[i]=n+1;//没有比他大的取n+1
t2.push(i);
}
else r[i]=t2.top(),t2.push(i);
}
int ans=0;
for(int i=1;i<=n;i++){
ans+=(r[i]-l[i]-2)*a[i];
ans+=(r[i]-i-1)*(i-l[i]-1)*a[i];
}
return ans;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int t;
cin>>t;
while(t--){
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sum+=solve();
for(int i=1;i<=n;i++) a[i]=-a[i];
sum+=solve();
cout<<sum<<endl;
}
}
题目:小A的柱状图
Q-小A的柱状图_2021秋季算法入门班第四章习题:堆栈队列单调栈等(重现赛)@IR101 (nowcoder.com)
解析:
本题需要求最大矩形面积,每个柱状图宽度和高度都是变化的,可以注意到,要与低的矩形相同的高,高的矩形可以切到和低的矩形一样高,但是高的矩形就不能切了,所以我们对每一个矩形找到它左右的比他小的值,一直切到比它小的那个矩形停下,这时候高就会是这个矩形的高,底就是左右两边比它小的值所夹的距离(可以用前缀和快速求他们两个之间的距离);
那么这左右两小值该如何找呢?其实用单调栈就可以实现,左值就从左往右遍历,栈不为空时,该数就和栈顶做比较,只要大于大于等于该数就踢出,一直找到小于它的数停止,再把该数入栈。一直这样就可以保证栈顶到栈底为单调递减。右值则是从右往左遍历。
求出左右值之后就对每一个数作为高,然后取面积最大。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
int h[1000006];
int a[1000006];
int num[1000006];
int l[1000006];
int r[1000006];
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int n;
cin>>n;
stack<int>t1,t2;
for(int i=1;i<=n;i++){
cin>>a[i];
num[i]=num[i-1]+a[i];
}
for(int i=1;i<=n;i++){
cin>>h[i];
}
for(int i=1;i<=n;i++){//
while(!t1.empty()&&h[t1.top()]>=h[i]){t1.pop();}
if(t1.empty()){
l[i]=0;
t1.push(i);
}
else l[i]=t1.top(),t1.push(i);
}
for(int i=n;i>0;i--){
while(!t2.empty()&&h[t2.top()]>=h[i]){t2.pop();}
if(t2.empty()){
r[i]=n+1;
t2.push(i);
}
else r[i]=t2.top(),t2.push(i);
}
int ma=0;
for(int i=1;i<=n;i++){
ma=max(ma,(num[r[i]-1]-num[l[i]])*h[i]);
}
cout<<ma<<endl;
return 0;
}
题目:装备合成
解析:
这是第一次写三分,三分是用于单峰/单谷函数,求极大值极小值的范围,有四个点把连续函数分为3段,每次观察中间两个点谁大谁小,拿求极大值举例,每次看mid,midd谁大,小的一边向另一边移动,直到达到自己可以判断的范围,再拿左值,到右值进行枚举判断。本题就是三分方法一,判断它的值(能合成的装备)的大小关系。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
int x,y;
int js(int a){
int sum=min((x-a*2)/4,(y-3*a));
return a+sum;
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
int t;
cin>>t;
while (t--)
{
cin>>x>>y;
int l=0,r=min(x/2,y/3),mid,midd;//三分方法1
while(r-l>2){
mid=l+(r-l)/3;//左
midd=r-(r-l)/3;//右
if(js(mid)>js(midd)){
r=midd-1;
}
else{
l=mid+1;
}
}
int ans=0;
for(int i=l;i<=r;i++){//方法1的件数
ans=max(ans,js(i));
}
cout<<ans<<endl;
}
return 0;
}