A-最大矩形
INPUT:
输入包含多组数据。每组数据用一个整数n来表示直方图中小矩形的个数,你可以假定1 <= n <= 100000. 然后接下来n个整数h1, …, hn, 满足 0 <= hi <= 1000000000. 这些数字表示直方图中从左到右每个小矩形的高度,每个小矩形的宽度为1。 测试数据以0结尾。
OUTPUT:
对于每组测试数据输出一行一个整数表示答案。
Sample Input:
7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0
Sample Output:
8
4000
题目分析:
这道题使用的是单调栈,单调栈中维护的是一个单调递增或者单调递减的数列,当一个元素入栈的时候,要将题以栈顶元素进行比较如果小于栈顶就弹出,这种数据结构能够找到第一个比该元素小的元素,所以在这道题中能够找到小于这个元素的第一个元素,就可以找到据悉那个延展的边界。
代码如下:
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int maxn = 1e5+100;
int t[maxn];
int L[maxn];
int R[maxn];
int n;
void solveR()
{
stack<int> p;
for(int i = 1; i<=n;i++)
{
while(!p.empty() && t[p.top()]>t[i])
{
R[p.top()] = i;
p.pop();
}
p.push(i);
}
while(!p.empty())
{
R[p.top()] = n+1;
p.pop();
}
}
void solveL()
{
stack<int> p;
for(int i = n;i>=1;i--)
{
while(!p.empty() && t[i]<t[p.top()])
{
L[p.top()] = i+1;
p.pop();
}
p.push(i);
}
while(!p.empty())
{
L[p.top()] = 1;
p.pop();
}
}
int main()
{
while(cin>>n)
{
if( n==0 ) break;
for(int i = 1;i<=n;i++)
scanf("%d",t+i);
solveR();
solveL();
int ans = 0,sum;
for(int i = 1;i<=n;i++)
{
sum = (R[i]-L[i])*t[i];
if(ans<=sum) ans =sum;
}
cout<<ans<<endl;
// for(int i = 1;i <=n;i++)
// {
// cout<<R[i];
// }
// cout<<endl;
// for(int i = 1;i <=n;i++)
// {
// cout<<L[i];
// }
// cout<<endl;
}
}
以上是助教讲过的两次单调栈找到两边的方法,其实该题完全可以用一次单调栈完成,原因是单调栈中相邻的两个数假设是a,b,a一定是小于b的第一个数,因为中间的数在b入栈的时候全部被弹出,弹出的数大于a且小于b,当栈为空时,证明现在的元素比之前的元素都小,那么他就能延伸到开始。
代码如下:
#include<iostream>
#include<stack>
using namespace std;
stack<long long> s;
long long n,ans,a[1e5+10];
long long now,l,r;
int main()
{
while(1)
{
cin>>n;ans = 0;
if(n==1) break;
for(int i = 1;i<=n;i++)
cin>>a[i];
for(int i = i;i<=n;i++)
{
if(s.empty()||a[i]>=a[s.top()])
s.push(i);
else{
//当
while(!s.empty()&&a[i]>=a[s.top()])
{
now = s.top();r = i-1;
s.pop();
//因为是单调栈,后面的元素一定比前面的元素大,所以前面的时边界
//而被弹出的元素一定是大于入栈的元素
if(s.empty())//栈为空,证明前面的元素都比现在的元素大,因为都弹出
l = 1;
else
l = s.top()+1;
ans = max(a[now]*(r-l+1),ans);
}
s.push(i);
}
}
//如果栈不为空 ,现在的TOP事最大值
if(!s.empty())
r = s.top();
while(!s.empty())
{
now = s.top();
s.pop();
if(s.empty())
l = 1;
else
l = s.top()+1;
ans = max(a[now]*(r-l+1),ans);
}
cout<<ans<<endl;
}
}
B - TT’s Magic Cat
题干:
Simple Input:
4 2
-3 6 8 4
4 4 -2
3 3 1
Simple Output:
-3 6 9 2
题目分析:
该题可以很简单的想到,暴力计算将每个区间的值都加n,但是由于数据量的庞大,时间是不允许我们这样做的,此时我们可以引入差分数组,差分数组,即除了第一个数之外,其他的数都是该数减去前一个数,那么原数组的第n个数的值就可以用差分数组的前n个数的累加获得,而如果对差分数组第a个数进行加减操作,后果就会使后面的n-a个数都进行同样的加减操作,这样就可以把对区间的修改变成对点的修改,速率大大提升。
代码如下:
#include<iostream>
#include<cstdio>
const int maxn = 300010;
using namespace std;
long long int a[maxn],b[maxn];
int main()
{
int n,m,l,r,num;
cin>>n>>m;
for(int i = 1;i<=n;i++)
scanf("%lld",&a[i]);
b[1] = a[1];
for(int i = 2;i<=n;i++)
b[i] = a[i] -a[i-1];
while(m--)
{
scanf("%d%d%d",&l,&r,&num);
b[l] += num;
b[r+1] -= num;
}
long long ans = 0;
for(int i = 1;i<=n;i++)
{
ans += b[i];
printf("%lld ",ans);
}
}
C-平衡字符串
题干;
一个长度为 n 的字符串 s,其中仅包含 ‘Q’, ‘W’, ‘E’, ‘R’ 四种字符。
如果四种字符在字符串中出现次数均为 n/4,则其为一个平衡字符串。
现可以将 s 中连续的一段子串替换成相同长度的只包含那四个字符的任意字符串,使其变为一个平衡字符串,问替换子串的最小长度?
如果 s 已经平衡则输出0。
Input:
一行字符表示给定的字符串s
Output:
一个整数表示答案
Example:
Input
QWER
Output
0
Input
QQWE
Output
1
Input
QQQW
Output
2
Input
QQQQ
Output
3
题目分析:
维护两个指针,两个指针之间划出一个区间。
对于区间以外的所有点,求出他们所有的点里,每个字母的出现次数sum1,sum2,sum3,sum4.
维护一个MAX值,其值等于sum里最大的那个。
因此我们可以得到其他每个字母距离出现次数最多的那个字母还差几个。
设ans=(MAX-sum1)+(MAX-sum2)+(MAX-sum3)+(MAX-sum4),则我们只需要找出至少ans个可以用来修改的字母就能够满足条件了。
那么这ans个点去哪找呢?——两个指针划出来的区间里。
只需要这个区间里的字母数量减去ans之后是0或者4的倍数即可。
如果当前区间长度满足条件,存起来,其可能是最终要求的答案,然后将l指针往前挪,即尝试缩小区间范围看看有没有更小的区间符合要求。
如果当前区间长度不满足条件,则将r指针往前挪,使区间变大看看有没有可能符合要求。
由于可能一开始的时候,不做任何变化这个字符串就符合要求,因此初始两指针都放在0号位置上。
代码如下:
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 200100;
int q[maxn],w[maxn],e[maxn],r[maxn];
string s;
int ans;
int main()
{
cin>>s;
int len = s.length();
ans = len;
if(s[0]=='Q') q[0] = 1;
if(s[0]=='W') w[0] = 1;
if(s[0]=='E') e[0] = 1;
if(s[0]=='R') r[0] = 1;
for(int i = 1;i<len;i++)
{
if(s[i]=='Q')
{
q[i] = q[i-1]+1;w[i] = w[i-1];e[i] = e[i-1];r[i] = r[i-1];
}
if(s[i]=='W')
{
q[i] = q[i-1];w[i] = w[i-1]+1;e[i] = e[i-1];r[i] = r[i-1];
}
if(s[i]=='E')
{
q[i] = q[i-1];w[i] = w[i-1];e[i] = e[i-1]+1;r[i] = r[i-1];
}
if(s[i]=='R')
{
q[i] = q[i-1];w[i] = w[i-1];e[i] = e[i-1];r[i] = r[i-1]+1;
}
}
int L = 0,R = 0;
while(L<len&&R<len)
{
int sumq = q[len-1] - (R==0?0:q[R]) +(L==0?0:q[L-1]);
int sumw = w[len-1] - (R==0?0:w[R]) +(L==0?0:w[L-1]);
int sume = e[len-1] - (R==0?0:e[R]) +(L==0?0:e[L-1]);
int sumr = r[len-1] - (R==0?0:r[R]) +(L==0?0:r[L-1]);
int maxl = max(max(sumq,sumw),max(sume,sumr));
int left = R-L+1-(maxl-sumq)-(maxl-sumw)-(maxl-sume)-(maxl-sumr);
if(left>=0&&left%4==0)
{
ans = min(ans,R-L+1);
L++;
}else{
R++;
}
}
cout<<ans<<endl;
}