其中有两幅图片是从上面的博客中获取的!!!
请尊重原创,大家可以点击上面链接找到对应的博客学习。
我只不过是借用两幅图片罢了
解决问题:
可以获取左边第一个或者右边第一个比当前位大或者小的数。
具体表现为:
1.最基础的应用就是给定一组数,针对每个数,寻找它和它右边第一个比它大的数之间有多少个数。
2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。
3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。
实现的伪代码:
const int N = 1e5+10;
int Sk[N],L[N],a[N],top;
//分别代表为:
//Sk[]:手工栈
//L[]:比当前为左边第一个大(小)
//a[]:原数组
//top:栈顶指针
//默认左边:0 , 右边: n+1 无限大(小)
for(int i=1;i<=n;i++){
while( top>=1 && a[Sk[top]] ** a[i] ){//把不合法的弹出栈
top -- ;
}
if(top){ //栈非空:栈顶元素就是我们要的左边第一个比当前值大(小)
L[i] = Sk[top];
}else{
L[i] = 1; //栈为空:左边为1
}
Sk[++top] = i; //下标压栈
}
对于序列【3,4,2,6,4,5,2,3】
找出左边第一个比他大的值的下标
用栈维护:
如果当前值比栈顶元素要大:弹栈
如果当前值比栈顶元素要小:压栈
综合来说:
就是先把不合法的弹栈,然后必然要压栈
牛都往右看,然后有所有牛,能看到牛的总个数
#include
#define For(i,L,R) for(int i=L;i<=R;i++)
using namespace std;
typedef long long ll;
const int N = 1e6+100;
ll a[N],St[N],R[N],top;
ll ans ;
int main()
{
ll n;
while(~scanf("%lld",&n)){
ans = 0;
For(i,1,n){
scanf("%lld",&a[i]);
}
top = 0;
for(int i=1;i<=n;i++){
while( top >= 1 && a[St[top]] <= a[i] ){
top--;
}
ans+=top;
St[++top] = i;
}
printf("%lld\n",ans);
}
return 0;
}
/*
6
10 3 7 4 12 2
*/
【题意】:
给你一个类似与柱状图一样,请问如果求出最大矩形,在柱状图中。
#include
using namespace std;
typedef long long ll;
const int N = 1e5+100;
ll a[N],L[N],R[N],St[N],top,n;
int main()
{
while(~scanf("%lld",&n),n){
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
top = 0;
for(int i=1;i<=n;i++){
while( top>=1 && a[i] <= a[St[top]] )
top--;
if( top == 0){
L[i] = 1;
}else{
L[i] = St[top] + 1;
}
St[++top] = i;
}
top = 0;
for(int i=n;i>=1;i--){
while( top>=1 && a[i] <= a[St[top]] )
top--;
if( top == 0){
R[i] = n;
}else{
R[i] = St[top]-1;
}
St[++top] = i;
}
ll ans = -1,tmp = 0;
for(int i=1;i<=n;i++){
tmp = a[i] * (R[i]-L[i]+1);
if ( tmp > ans ){
ans = tmp ;
}
}
printf("%lld\n",ans);
}
return 0;
}
/*
7 2 1 4 5 1 3 3
*/
【题意】:
给你一个01矩阵,请问最大矩形是多少,这个矩形里面都是1.
这个题目非常有意思,真的不认真想是想不出来的。
看了网上题解:首先进行预处理,把所有01往上延伸。
譬如矩形:
0 0 1 0
1 1 1 0
1 1 1 1
0 0 1 0
变成:
0 0 4 0
2 2 3 0
1 1 2 1
0 0 1 0
这样的做法就可以枚举每一行,然后看每一行中的左右:问题就转变成上一个题目一样了。
#include
#include
#include
using namespace std;
typedef long long ll ;
const int N = 2e3+10;
ll a[N][N],n,m;
ll L[N],R[N],St[N],top;
int main()
{
while(scanf("%lld%lld",&n,&m)!=EOF){
memset(a,0,sizeof(a));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
}
for(int i=n-1;i>=1;i--){
for(int j=1;j<=m;j++){
if( a[i][j] )
a[i][j] = a[i][j] + a[i+1][j];
}
}
/*
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d%c",a[i][j],j==m?'\n':' ');
}
}
*/
ll ans = -0x3f3f3f3f , tmp ;
for(int i=1;i<=n;i++){
top = 0;
for(int j=1;j<=m;j++){
while( top>=1 && a[i][j]<=a[i][ St[top] ] ) top--;
if( top ) L[j] = St[top]+1;
else L[j] = 1;
St[++top] = j;
}
top = 0;
for(int j=m;j>=1;j--){
while( top>=1 && a[i][j]<=a[i][ St[top] ] ) top--;
if( top ) R[j] = St[top]-1;
else R[j] = m;
St[++top] = j;
tmp = (a[i][j]) * ( R[j] - L[j] + 1 ) ;
ans = max( ans ,tmp );
}
}
printf("%lld\n",ans);
}
return 0;
}
【题意】:
区间最小值乘以区间和,求最大值
#include
#include
#define For(i,L,R) for(int i=L;i<=R;i++)
using namespace std;
typedef long long ll;
const int N = 1e5+100;
ll sum[N],a[N],R[N],L[N],St[N],top;
int main()
{
int n;
while(scanf("%d",&n)!=EOF){
For(i,1,n) scanf("%d",&a[i]),
sum[i] = sum[i-1]+a[i],
L[i] = R[i] = 0;
top = 0;
For(i,1,n){
while( top>=1 && a[i] <= a[St[top]] ){
top -- ;
}
if(top){
L[i] = St[top] + 1;
}else{
L[i] = 1;
}
St[++top] = i;
}
top = 0 ;
for(int i=n;i>=1;i--){
while( top>=1 && a[i] <= a[St[top]] ){
top -- ;
}
if(top){
R[i] = St[top]-1;
}else{
R[i] = n;
}
St[++top] = i;
}
ll tmp = 0 , ans = -0x3f3f3f3f ,ansL,ansR;
for(int i=1;i<=n;i++){
tmp = ( sum[R[i]] - sum[L[i]-1] ) *a[i];
if( ans < tmp ){
ans = tmp;
ansL = L[i];
ansR = R[i];
}
}
printf("%lld\n%lld %lld\n",ans,ansL,ansR);
}
return 0;
}