A题 最大矩形
题目描述
给一个直方图,求直方图中的最大矩形的面积。例如,下面这个图片中直方图的高度从左到右分别是2, 1, 4, 5, 1, 3, 3, 他们的宽都是1,其中最大的矩形是阴影部分。
input:
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
output:
对于每组测试数据输出一行一个整数表示答案。
样例:
in:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
out:
8
4000
思路
利用单调栈的思想,得到每一个高度的左右边第一个小于该高度的索引,索引差乘以高度就是矩形面积,比较每一次的大小,输出最大值。
这里具体说一下如何实现单调栈,我使用STL中的stack结构,利用两· 个数组分别储存左右第一个小于该高度的索引,计算右索引时用到的是非递减单调栈,就是说当栈顶元素小于等于待插入元素是才能插入,否则弹栈继续该判断,直至栈空。栈顶元素弹栈,也就意味着栈顶元素已经遇到了右边第一个小于自身的数,更新R[st.top()]。所有的点都加入后,将栈里的所有元素的R值更新为n,即可以延伸到最后。获取左坐标的方法一样,就点的插入顺序变为逆序,即从第n个开始入栈。
代码
#include<iostream>
#include<stdio.h>
#include<stack>
#include<string.h>
#include<algorithm>
#define Max 100000
#define ll long long
using namespace std;
stack<int>st;
ll a[Max+2];
int L[Max+1],R[Max+1],n;
int main()
{
scanf("%d",&n);
while(n)
{
for(int i=0;i<n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
{
while(!st.empty()&&a[st.top()]>a[i])
{
R[st.top()]=i;
st.pop();
}
st.push(i);
}
while(!st.empty())
{
R[st.top()]=n;
st.pop();
}
for(int i=n-1;i>=0;i--)
{
while(!st.empty()&&a[st.top()]>a[i])
{
L[st.top()]=i;
st.pop();
}
st.push(i);
}
while(!st.empty())
{
L[st.top()]=-1;
st.pop();
}
ll mmm=(R[0]-L[0]-1)*a[0];
for(int i=1;i<n;i++)
{
if(mmm<(R[i]-L[i]-1)*a[i])
mmm=(R[i]-L[i]-1)*a[i];
}
printf("%lld\n",mmm);
cin>>n;
}
return 0;
}
总结
刚开始没有意识是到数据范围,虽然每个点的数据范围为1e9,但是乘以宽度之后就可能超出int范围,后来改成long long结构后也WA了,百思不得其解,还是基本功不扎实啊,占位符lld错写成了ld,导致连续WA了好几遍。
B题 TT’s Magic Cat
题目描述
Thanks to everyone’s help last week, TT finally got a cute cat. But what TT didn’t expect is that this is a magic cat.
One day, the magic cat decided to investigate TT’s ability by giving a problem to him. That is select nn cities from the world map, and a[i] represents the asset value owned by the ii-th city.
Then the magic cat will perform several operations. Each turn is to choose the city in the interval [l,r] and increase their asset value by c. And finally, it is required to give the asset value of each city after q operations.
Could you help TT find the answer?
input:
The first line contains two integers n,q(1≤n,q≤2·105) — the number of cities and operations.
The second line contains elements of the sequence aa: integer numbers a1,a2,…,an (−106≤ai≤106)
Then q lines follow, each line represents an operation. The ii-th line contains three integers l,r and cc (1≤l≤r≤n,−105≤c≤105) for the i-th operation.
output:
Print n integers a1,a2,…,an one per line, and ai should be equal to the final asset value of the i-th city.
样例:
Input
4 2
-3 6 8 4
4 4 -2
3 3 1
Output
-3 6 9 2
思路
这里采用差分的思想将区间操作转化为对点的操作,首先构造差分数组
p[1]=a[1] p[i]=a[i]-a[i-1]
这样对a的[l,r]区间的增加c的操作就转化为对p[l]+=c,p[r+1]-=c。
最后利用p数组复原出新的a数组然后输出。
代码
#include<iostream>
#include<stdio.h>
#include<algorithm>
#define Max 300000
#define ll long long
using namespace std;
ll a[Max+2],n,q,t1,t2,c,s, L[Max+2];
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
L[1]=a[1];
for(int i=2;i<=n;i++)
{
L[i]=a[i]-a[i-1];
}
while(q--)
{
scanf("%lld%lld%lld",&t1,&t2,&c);
L[t1]+=c;
L[t2+1]-=c;
}
// printf("%d ",L[1]);
for(int i=1;i<=n;i++)
{
s+=L[i];
printf("%lld ",s);
}
printf("\n");
// system("pause");
return 0;
}
总结
这道题也是错在了数据范围上,栽在了A的坑上。
C题 平衡字符串
题目描述
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input一行字符表示给定的字符串s
Output一个整数表示答案
样例
Input
QWER
output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
思路
这里采用尺取法的思想,维护一个双指针,对给定 [L, R],判断是否满足要求? 用 sum1, sum2, sum3, sum4 分别记录不包含区间 [L, R] 这一段时,字符 ‘A’, ‘B’, ‘C’, ‘D’ 的个数 , 先通过替换使 4 类字符数量一致,再判断剩余空闲位置是否 为 4 的倍数 • MAX = max(sum1, sum2, sum3, sum4) , TOTAL = R – L + 1 , FREE = TOTAL [(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4)] • 若 FREE ≥0 且为 4 的倍数,则满足要求L++;否则不满足,R++。输出最小的total值。
代码
#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#define MAX 100000
using namespace std;
string str;
int s1,s2,s3,s4,Max,total,Free,L,R,jj=MAX;
int main()
{
//scanf("%s",&str[0]);
cin>>str;
for(int i=0;i<str.size();i++)
{
if(str[i]=='Q')s1++;
else if(str[i]=='W')s2++;
else if(str[i]=='E')s3++;
else if(str[i]=='R')s4++;
}
if(s1==s2&&s2==s3&&s3==s4)
{
printf("0\n");//exit(0);
// system("pause");
return 0;
}
if(str[R]=='Q')s1--;
else if(str[R]=='W')s2--;
else if(str[R]=='E')s3--;
else if(str[R]=='R')s4--;
//R=0 开始
while(R<str.size())
{
Max = max(max(s1,s2),max(s3,s4));
total = R-L+1;
Free =total-((Max-s1)+(Max-s2)+(Max-s3)+(Max-s4));
if(Free>=0&&Free%4==0)//满足条件
{
jj=min(jj,total);
if(str[L]=='Q')s1++;
else if(str[L]=='W')s2++;
else if(str[L]=='E')s3++;
else if(str[L]=='R')s4++;
L++;
}
else
{
R++;
if(str[R]=='Q')s1--;
else if(str[R]=='W')s2--;
else if(str[R]=='E')s3--;
else if(str[R]=='R')s4--;
}
}
printf("%d\n",jj);
// system("pause");
return 0;
}
总结
对尺取法的理解出了些问题,else里面写错WA了几次,加上对四个计数变量的更新就A了
D滑动窗口
问题描述
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.
例如:
数列是[1 3 -1 -3 5 3 6 7], 其中 k 等于 3.
input:
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
Output:
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
样例:
input:
8 3
1 3 -1 -3 5 3 6 7
putput:
-1 -3 -3 -3 3 3
3 3 5 5 6 7
思路
这里用两个单调队列分别计算区域最小值和最大值,这里以区域最小值为例。先往队列中按照非递减序进行入队。对于之后的元素按照同样的规则入队,只不过每次操作都将队首元素作为该区间最小值,最大值原理一样就是入队规则变成小于队尾元素。
代码
#include<iostream>
#include<stdio.h>
#include<deque>
#include<string.h>
#include<algorithm>
#define Max 1000000
using namespace std;
deque<int>qu1,qu2;
int a[Max+2],n,k,c=1,m;
int mi[Max+1],ma[Max+1];
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<k-1;i++)
{
while(!qu1.empty()&&a[qu1.back()]>a[i])//a[st.top()]>a[i])
{
qu1.pop_back();
}
qu1.push_back(i);
while(!qu2.empty()&&a[qu2.back()]<a[i])//a[st.top()]>a[i])
{
qu2.pop_back();
}
qu2.push_back(i);
}
for(int i=k-1;i<n;i++,c++)
{
while(!qu1.empty()&&a[qu1.back()]>a[i])//a[st.top()]>a[i])
{
qu1.pop_back();
}
qu1.push_back(i);
while(!qu2.empty()&&a[qu2.back()]<a[i])//a[st.top()]>a[i])
{
qu2.pop_back();
}
qu2.push_back(i);
mi[m]=qu1.front();
ma[m++]=qu2.front();
if(qu1.front()<=i-k+1)qu1.pop_front();
if(qu2.front()<=i-k+1)qu2.pop_front();
}
for(int i=0;i<m;i++)
printf("%d ",a[mi[i]]);
printf("\n");
for(int i=0;i<m;i++)
printf("%d ",a[ma[i]]);
printf("\n");
// system("pause");
return 0;
}
总结
在判定队首元素是否应该出队的时候犯了一点小错,因为队列中存储的是数组的索引,所以只需要判断该值与i的差是否大于k就好了