Histogram LightOJ

来源:互联网 发布:视频编辑软件排行 编辑:程序博客网 时间:2024/06/08 01:25

题面

题意

有一串由矩形组成的图,先要求出这幅图中所能容纳的最大矩形的面积

总体思路

求出以各个小矩形高度为宽所能达到的最大面积,此时我们称这个最大矩形被这个小矩形统治,我们只需求出每一个小矩形统治的最大矩形的左端点和右端点.

法1(单调栈)

方法解释

让矩形依次入栈,当入栈矩形小于栈顶时,说明此时的栈顶不能统治该矩形,其右端点即为入栈矩形的位置-1.最后将让元素全部出栈,它们的右端点均为这个图的右端点
当矩形入栈后,其下面的矩形必然小于等于它,若小于,则其左端点为它下面那个矩形的位置+1,若等于,则其左端点为其下面矩形的左端点.第一个元素的左端点为整个图的左端点.

代码

#include<bits/stdc++.h>#define P pair<int,int>#define N 100100using namespace std;int n,T,TT;int get(vector <int> a){    int i,k,le[N],ri[N],mx=0;    P tmp;    stack<P> st;    le[0]=0;    a.push_back(0);    tmp.first=tmp.second=0;    st.push(tmp);    for(i=1;i<=n+1;i++)    {        k=st.top().first;        while(a[i]<k&&!st.empty())        {            ri[st.top().second]=i-1;            st.pop();            k=st.top().first;        }        if(a[i]==k)        le[i]=le[st.top().second];        else        le[i]=st.top().second+1;        tmp.first=a[i];        tmp.second=i;        st.push(tmp);    }    for(i=1;i<=n;i++)    {        mx=max(a[i]*(ri[i]-le[i]+1),mx);    }    return mx;}int main(){    int i,j,p,mx=0;    vector<int> a;    cin>>T;    TT=T;    while(T--)    {        a.clear();        scanf("%d",&n);//      cout<<n;        a.push_back(0);        for(i=1;i<=n;i++)        {            scanf("%d",&p);            a.push_back(p);        }//      get(a);        printf("Case %d: %d\n",TT-T,get(a));    }}

法2

方法解释

求左端点:从小到大枚举,若每个矩形左端点的初始值为自身,若它小于等于它左端点减一的矩形的左端点,则将其左端点更新为那个矩形的左端点,如此循环更新直到大于那个矩形或为一时停止,每一个点都这样操作一遍即可.
求右端点:从大到小枚举,与左端点相同

此方法每一次操作都将两块连起来,类似并查集,故复杂度为O(n).

代码

#include<bits/stdc++.h>#define N 100010using namespace std;int a[N],n,le[N],ri[N],mx,T,TT;int main(){    int i,j;    cin>>T;    TT=T;    while(T--)    {        mx=0;        scanf("%d",&n);        for(i=1;i<=n;i++)        {            scanf("%d",&a[i]);            le[i]=i;            while(le[i]>1&&a[i]<=a[le[i]-1])            {                le[i]=le[le[i]-1];            }        }        for(i=n;i>=1;i--)        {            ri[i]=i;            while(ri[i]<n&&a[i]<=a[ri[i]+1])            {                ri[i]=ri[ri[i]+1];            }        }        for(i=1;i<=n;i++)        {            mx=max(a[i]*(ri[i]-le[i]+1),mx);        }        printf("Case %d: %d\n",TT-T,mx);    }}

法3

方法解释

首先用n*log(n)处理出以一个点为左端点,2^i为长度的一段数最小值,处理和查找时记得记录位置。
处理方法为:长度为2^i的最小值为两个长度为2^(i-1)的较小值,以此递推下去。
查找方法:见下图:
方法
两个红色部分的最小值即为这一段的最小值
预处理之后,每次找到最小值的位置,以此计算它统治的矩形来更新答案,并以它为中心分成两段,分别继续查找。因为每个点都被查了一次,故复杂度为O(n).

代码

#include<bits/stdc++.h>#define N 50010#define P pair<int,int>using namespace std;int num[N],n,dp[N][100],k[N][100],T,TT,l,r,ans;int log(int u){    int res=0;    while(u)    {        res++;        u>>=1;    }    return res;}int ask(int u,int v){    int len;    len=v-u+1;    len=log(len)-1;    if(dp[u][len]<dp[v-(1 << len)+1][len])    {        return k[u][len];    }    else    {        return k[v-(1 <<len)+1][len];    }}void find(int u,int r){    if(u>r) return;    int i,tmp;    tmp=ask(u,r);    ans=max(ans,num[tmp]*(r-u+1));    find(u,tmp-1);    find(tmp+1,r);}int main(){    cin>>T;    TT=T;    while(T--)    {        int i,j;        scanf("%d",&n);        for(i=1;i<=n;i++)        {            scanf("%d",&num[i]);            dp[i][0]=num[i];            k[i][0]=i;        }        for(i=1;(1 << i)<=n;i++)        {            for(j=1;j+(1 << i)-1<=n;j++)            {                if(dp[j][i-1]<dp[j+(1 << (i-1))][i-1])                {                    dp[j][i]=dp[j][i-1];                    k[j][i]=k[j][i-1];                }                else                {                    dp[j][i]=dp[j+(1 << (i-1))][i-1];                    k[j][i]=k[j+(1 << (i-1))][i-1];                }            }        }        ans=0;        find(1,n);        printf("Case %d: %d\n",TT-T,ans);    }}

法4

方法解释

首先建一棵笛卡尔树,利用其性质进行分制,复杂度为O(n)。

代码

#include<bits/stdc++.h>#define N 50010using namespace std;struct Tree{    int lef,rig;}tree[N];int T,TT,num[N],fa[N],ans,n;void find(int now,int l,int r){    if(now==-1) return;    ans=max(num[now]*(r-l+1),ans);    find(tree[now].lef,l,now-1);    find(tree[now].rig,now+1,r);}int main(){    int i,j,now,k,gen;    cin>>T;    TT=T;    while(T--)    {        scanf("%d",&n);        for(i=1;i<=n;i++)        {            tree[i].lef=tree[i].rig=-1;            scanf("%d",&num[i]);        }        now=1;        gen=1;        fa[1]=-1;        for(i=2;i<=n;i++)        {            k=now;            while(num[i]<num[k]&&k!=-1)            {                k=fa[k];            }            if(k==-1)            {                tree[i].lef=gen;                fa[gen]=i;                fa[i]=-1;                now=gen=i;            }            else            {                tree[i].lef=tree[k].rig;                tree[k].rig=i;                now=i;                fa[i]=k;            }        }        ans=0;        find(gen,1,n);        printf("Case %d: %d\n",TT-T,ans);    }}