简单题也是要回顾一下,不然赛场上这个做法久了不用,就会忘了这种做法。
单调栈:可以O(n)找到左边与右边第一个大于其或者小于其的数字,记得维护的时候只有出栈的时候那个下标才被维护,
并且新进去的元素继承出来元素的边界。
或者说是找到区间最小值为x的那个区间的l与r
hdu1506:
裸题,对一个柱状图求覆盖最大的举行面积。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1e5+7;
const ll INF=1e9+7;
ll q1[maxn],q2[maxn],q3[maxn];
struct ttt{
ll key,id;
};
ttt s1[maxn];
int main(){
ll i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
//freopen("in.txt","r",stdin);
//freopen("out2.txt","w",stdout);
ll n,m;
ll id;
while(scanf("%lld",&n)==1){
if(n==0)break;
ll top=0;
ll res=0;
for(i=1;i<=n;i++){
scanf("%lld",&t1);
id=i;
if(top==0){
s1[++top].id=i;
s1[top].key=t1;
}else{
while(top>=1&&t1<s1[top].key){ //那么每次出栈的就是那个结果
res=max(res,s1[top].key*(i-s1[top].id)); //被弹出的时候才运算
id=s1[top].id; //每次都会把id替换拿走
top--;
}
if(top==0||s1[top].key<t1){
s1[++top].key=t1;
s1[top].id=id;
}
}
}
while(top){
res=max(res,s1[top].key*(n-s1[top].id+1));top--;
}
cout << res<< endl;
}
return 0;
}
HDU5033(有点难度,好题):
给一个二维平面,x轴上有许多高度为a[i]的墙,问一些x,其能看到天空的角度有多大。
显然对于每个点,其能看到的角度,当只考虑左边时,其维护的墙壁一定是单调递减,在单调递减的情况下,斜率有凸包和凹包,显然斜率变大的凸包的单调栈的栈顶才是该点能看到的点。
1、不仅仅是比较最近的两个直线的y与x的差,而应该比较斜率,因为2直线斜率是固定的值,而y/x只比较了相邻的两条直线
2、注意每个变量的含义
atan(),求某个角度的值。然后*180/PI就是角度
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
const int INF=1e9+7;
#define Pi acos(-1.0)
struct ttt{
double x,y;
int id;
double l,r;
int type;
};
vector<int>v;
ttt q1[maxn];
ttt S[maxn];
int cmp1(ttt x,ttt y){
return x.x<y.x;
}
double res[maxn];
int main(){
int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
int n,m;
//freopen("in.txt","r",stdin);
//freopen("out2.txt","w",stdout);
double g1,g2,g3,g4;
int T;
scanf("%d",&T);
int num=0;
while(T--){
num++;
printf("Case #%d:\n",num);
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%lf %lf",&q1[i].x,&q1[i].y);
q1[i].type=1;
}
scanf("%d",&m);
for(i=n+1,j=1;j<=m;j++,i++){
scanf("%lf",&q1[i].x);
q1[i].id=j;
q1[i].type=2;
}
sort(q1+1,q1+1+n+m,cmp1);
int top=0;
for(i=1;i<=n+m;i++){
while(top>=2){
if(S[top].y/(q1[i].x-S[top].x)<=(S[top-1].y-S[top].y)/(S[top].x-S[top-1].x)){
top--;
}else{
break;
}
}
if(q1[i].type==2){ //记录结果
q1[i].l=atan(S[top].y/(q1[i].x-S[top].x))*180.0/Pi;
}else{
while(top&&S[top].y<=q1[i].y){
top--; //之前那个不要了
}
while(top>=2&&(S[top-1].y-S[top].y)/(S[top].x-S[top-1].x)>=(S[top].y-q1[i].y)/(q1[i].x-S[top].x)){
top--;
}
S[++top].y=q1[i].y;
S[top].x=q1[i].x;
}
}
top=0;
for(i=n+m;i>=1;i--){
while(top>=2){
if(S[top].y/(S[top].x-q1[i].x)<=(S[top-1].y-S[top].y)/(S[top-1].x-S[top].x)){
top--;
}else{
break;
}
}
if(q1[i].type==2){ //记录结果
q1[i].r=atan(S[top].y/(S[top].x-q1[i].x))*180.0/Pi;
res[q1[i].id]=180.0-(q1[i].r+q1[i].l);
}else{
while(top&&S[top].y<=q1[i].y){
top--; //之前那个不要了
}
while(top>=2&&(S[top-1].y-S[top].y)/(S[top-1].x-S[top].x)>=(S[top].y-q1[i].y)/(S[top].x-q1[i].x)){
top--;
}
S[++top].y=q1[i].y;
S[top].x=q1[i].x;
}
}
for(i=1;i<=m;i++)
printf("%.10f\n",res[i]);
}
return 0;
}
poj2796:
选择一个区间[l,r],这个区间的和*区间最小值为答案,问最大值是多少。
刚开始一看毫无思路,但是这种n2的区间,显然通过某个方法得到对每个值询问的时候直接得到其前面所有区间的值。
这里因为是找的单调栈专题,所以相对会相到。
显然栈中为单调递增才可行,因为如果单调递减必然不满足,当单调递增,每次出栈考虑这个栈的左边界,进行一个区间*值,那么获得的值取个最大就是答案了。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const ll maxn=2e5+7;
const ll INF=1e9+7;
#define Pi acos(-1.0)
ll sum1[maxn];
ll q1[maxn];
struct ttt{
ll num;
ll key;
};
ttt S[maxn];
ll top;
int main(){
ll i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
ll n,m;
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
while(scanf("%lld",&n)==1){
for(i=1;i<=n;i++){
scanf("%lld",&q1[i]);
sum1[i]=sum1[i-1]+q1[i];
}
sum1[n+1]=sum1[n];
q1[n+1]=-1;
top=1;
S[top].num=1;S[top].key=q1[1];
ll res=-1;
ll l,r;
for(i=2;i<=n+1;i++){
t1=i;
while(top>0&&q1[i]<S[top].key){
if(S[top].key*(sum1[i-1]-sum1[S[top].num-1])>res){
res=S[top].key*(sum1[i-1]-sum1[S[top].num-1]);
l=S[top].num;r=i-1;
}
t1=S[top].num;top--;
}
S[++top].num=t1;
S[top].key=q1[i];
}
printf("%lld\n",res);
printf("%lld %lld\n",l,r);
}
return 0;
}