poj2796 维护区间栈//单调栈
http://poj.org/problem?id=2796
题意:给你一段区间,需要你求出(在这段区间之类的最小值*这段区间所有元素之和)的最大值......
例如:
6
3 1 6 4 5 2
以4为最小值,向左右延伸,6 4 5 值为60.......
思路:解决完为这道题目,我才真正明白了单调栈的原理,它就是以某一个值为最小(最大)值,向这个值的两侧延伸,遇到大于它(小于它)的值,就将它延伸的范围扩大,当然,一般来说,要这样做的算法复杂度为o(n^2),但是借助栈这个玩意,维护其单调增(减),就可以在o(n)的时间复杂度解决这个问题。将一元素加入栈时,先判断它是否大于(小于)栈顶元素,若是大于(小于)栈顶元素,加入栈。(从这里开始只讲维护单调增栈)否则,将栈顶元素出栈,直到栈顶元素小于要加入栈的元素,在此过程中,需要维护向前延伸和向后延伸的问题,当要加入栈的元素之前有n个栈元素出栈,那么说明这n个出栈的元素都会大于或者等于要入栈的元素,此时,我们需要维护入栈元素可以向前延伸多少个元素(相当于记录它的前面有多少个元素比它大),而每个栈顶元素要向出栈了的元素延伸,因为在出栈了的元素一定是比它的大的元素(根据我维护的是单调增栈)......这样,就在o(n)的时间复杂度内解决了上述问题.........
例如:3 1 6 4 5 2
(3,1,1) (1,2,2) (6,3,3) (4,4,4) (5,5,5) (2,6,6)
首先每个元素自己本身的前后延伸都为1,把3加入栈,1<3,把3出栈,用1的前延伸加上3的前延伸,如此变为(1,1,2),6<1,入栈,变成(1,1,2)(6,3,3),
4<6,将6出栈,4向前延伸,1向后延伸变成(1,1,3) (4,3,4)
5>4,入栈,变成(1,1,3)(4,3,4)(5,5,5)
2<5,5出栈,2向前延伸,4向后延伸,变成(1,1,3)(4,3,5) 2还未入栈(2,5,6)
2<4,4出栈,2向前延伸,1向后延伸,变成(1,1,5) (2,3,6).....
依次类推,会发现最大的结果在(4,3,5)这里这意味着,以4为最小值的区间范围为3————5,也就是6 4 5
AC代码:
#include<cstdio>
#include<algorithm>
#include<stack>
using namespace std;
int a[];
long long s[];
struct Node
{
int k,l,r;
int num;
};
long long sum,ans;
int main()
{
int n;
while(scanf("%d",&n)==)
{
int x=,y=;
sum=-;
ans=-;
s[]=;
for(int i=; i<=n; i++)
{
scanf("%d",&a[i]);
if(i==) s[i]=a[i];
else s[i]=s[i-]+a[i];
}
Node t,t1;
stack<Node>q;
t.num=a[];
t.k=t.l=t.r=;
q.push(t);
for(int i=; i<=n; i++)
{
t1.num=a[i];
t1.k=i;
t1.l=t1.r=;
while(!q.empty() && t1.num<=q.top().num)
{
t=q.top();
q.pop();
t1.l+=t.l;
if(!q.empty()) q.top().r+=t.r;
ans=t.num*(s[t.k+t.r-]-s[t.k-t.l]);
if(ans>=sum)
{
sum=ans;
x=t.k-t.l+;
y=t.k+t.r-;
}
}
q.push(t1);
}
while(!q.empty())
{
t=q.top();
q.pop();
if(!q.empty()) q.top().r+=t.r;
ans=t.num*(s[t.k+t.r-]-s[t.k-t.l]);
if(ans>=sum)
{
sum=ans;
x=t.k-t.l+;
y=t.k+t.r-;
}
}
if(n==)x=y=;
printf("%lld\n%d %d\n",sum,x,y);
}
return ;
}
网上写的比较好的代码:
#include<iostream>
#include<stack>
#include<stdio.h>
using namespace std;
#define maxx 110000
__int64 str[maxx],t[maxx];
struct node
{
__int64 num,pre,next; //num记录数值,pre记录向前延伸多少个,next记录向后延伸多少个,k记录本身所处的位置
__int64 k;
};
int main()
{
int n;
while(scanf("%d",&n)==)
{
stack<node>Q;
node tmp;
__int64 ans=-,sum=-,num;
str[]=;
for(__int64 i=; i<=n; i++)
{
scanf("%I64d",&t[i]);
if(i==)
str[i]=t[i];
else
str[i]=str[i-]+t[i];
}
tmp.num=t[];
tmp.pre=;
tmp.next=;
tmp.k=;
Q.push(tmp);
__int64 x=,y=;
for(__int64 i=; i<=n; i++)
{
node tmp1;
tmp1.num=t[i];
tmp1.pre=tmp1.next=;
tmp1.k=i;
while(!Q.empty()&&tmp1.num<=Q.top().num)
{
tmp=Q.top();
Q.pop();
if(!Q.empty())
Q.top().next+=tmp.next;
tmp1.pre+=tmp.pre;
ans=tmp.num*(str[tmp.k+tmp.next-]-str[tmp.k-tmp.pre]);
if(ans>=sum)
{
sum=ans;
x=tmp.k-tmp.pre+;
y=tmp.k+tmp.next-;
}
}
Q.push(tmp1);
}
while(!Q.empty())
{
tmp=Q.top();
Q.pop();
if(!Q.empty())
Q.top().next+=tmp.next;
ans=tmp.num*(str[tmp.k+tmp.next-]-str[tmp.k-tmp.pre]);
if(ans>=sum)
{
sum=ans;
x=tmp.k-tmp.pre+;
y=tmp.k+tmp.next-;
}
} if(n==)x=y=;
printf("%I64d\n%I64d %I64d\n",sum,x,y);
}
return ;
}
不用栈:
//两种不同的代码,这种是不用栈的,代码少,好理解
#include <stdio.h>
#define N 100001
int lef[N],rig[N];
__int64 sum[N],a[N];
int main()
{
int i,j,n;
scanf("%d",&n);
for(i = ; i <= n; ++i)
{
scanf("%I64d",a + i);
sum[i] = sum[i-] + a[i];
lef[i] = rig[i] = i;
}
for(i = ; i <= n; ++i)
while(lef[i] > && a[lef[i]-] >= a[i])
lef[i] = lef[lef[i] - ];
for(i = n-; i; --i)
while(rig[i] < n && a[rig[i]+] >= a[i])
rig[i] = rig[rig[i] + ];
int ll ,rr ; ///答案可以为0,res初始为-1
__int64 res = -,tmp;
for(i = ; i <= n; ++i)
{
tmp = a[i] * (sum[rig[i]] - sum[lef[i]-]);
if(tmp > res)
{
res = tmp;
ll = lef[i];
rr = rig[i];
}
}
printf("%I64d\n%d %d\n",res,ll,rr);
return ;
}
poj2796 维护区间栈//单调栈的更多相关文章
- 51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线
区间计数 基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下: Ans:=Σni=1Σnj=i[max{ ...
- 【POJ2796】Feel Good 单调栈
题目大意:给定一个长度为 N 的序列,求任意区间 [ l , r ] 中最小的\(min\{v[i],i\in[l,r] \}*\Sigma_{i=l}^rv[i]\). 题解:这是一道具有标准单调栈 ...
- 51nod 1962区间计数(单调栈加二分)
题目要求是求出两个序列中处于相同位置区间并且最大值相同的区间个数,我们最直观的感受就是求出每个区间的最大值,这个可以O(N)的求,利用单调栈求出每个数作为最大值能够覆盖的区间. 然后我们可以在进行单调 ...
- POJ2796 Feel Good(单调栈)
题意:给一个非负整数序列,求哪一段区间的权值最大,区间的权值=区间所有数的和×区间最小的数. 用单调非递减栈在O(n)计算出序列每个数作为最小值能向左和向右延伸到的位置,然后O(n)枚举每个数利用前缀 ...
- [BZOJ 2957]楼房重建(THU2013集训)(线段树维护单调栈)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2957 分析: 根据题意,就是比较斜率大小 只看一段区间的话,那么这段区间能看见的楼房数量就是这 ...
- BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)
BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
- 【POJ3250】Bad Hair Day 单调栈
题目大意:给定一个由 N 个数组成的序列,求以每个序列为基准,向右最大有多少个数字都比它小. 单调栈 单调栈中维护的是数组的下标. 单调栈在每个元素出栈时统计该出栈元素的答案贡献或对应的值. 单调栈主 ...
- 【learning】 单调队列与单调栈用法详解
1.单调栈 单调栈是指一个栈内部的元素具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈. 其具有以下两个性质: 1,满足栈底到栈顶的元素具有严格单调性. 2,满足栈的先进后出特性,越靠近栈顶的 ...
随机推荐
- codeforces 519C. A and B and Team Training 解题报告
题目链接:http://codeforces.com/contest/519/problem/C 题目意思:给出 n 个 experienced participants 和 m 个 newbie ...
- jquery格式化时间
使用方法: new Date().format("yyyy-MM-dd hh:mm:ss"); 格式: Date.prototype.format = function (form ...
- (译)利用ASP.NET加密和解密Web.config中连接字符串
介绍 这篇文章我将介绍如何利用ASP.NET来加密和解密Web.config中连接字符串 背景描述 在以前的博客中,我写了许多关于介绍 Asp.net, Gridview, SQL Server, A ...
- 【leetcode】Word Search II(hard)★
Given a 2D board and a list of words from the dictionary, find all words in the board. Each word mus ...
- August 26th 2016 Week 35th Friday
It always seems impossible until it's done. 在事情未完成之前,一切都看似不可能. When I was young, once I had to lift ...
- August 8th 2016, Week 33rd Monday
Everything is going on, but don't give up trying. 万事随缘,但不要放弃努力. Every time when I want to give up, y ...
- osgi学习
Bundle可以被动态地安装.启动.停止和卸载.Bundle是服务(Service)和组件(Component)的载体.在OSGi中,每个Bundle都有自己独立于其他Bundle的ClassLoad ...
- chaper3_exerise_Uva1368_DNA序列
#include<iostream> #include<stdio.h> #include<cmath> #include<string> #inclu ...
- python基础——获取对象信息
python基础——获取对象信息 当我们拿到一个对象的引用时,如何知道这个对象是什么类型.有哪些方法呢? 使用type() 首先,我们来判断对象类型,使用type()函数: 基本类型都可以用type( ...
- JS返回上一页
<button onclick="javascript:history.go(-1);">返回上一页</button> <button oncli ...