poj 2796
http://poj.org/problem?id=2796
hdu 1506
http://acm.hdu.edu.cn/showproblem.php?pid=1506
这两个题一个套路 所以放在一起写了
poj 2796
题意:给出一个长度n的序列 求 max(一个区间的和乘上该区间的最小值 )
我们可以这样 遍历每一个点 把每个点作为一个区间的最小值 去维护一下左右边界
那么在加上一个前缀和处理 遍历一遍就可以得出答案了
那么仔细想想 当前点最小 向左边去找 一直到最左边的数大于该数
右边也同理
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
ll a[1000005];
ll sum[1000005];
int l[1000005], r[1000005];
int main()
{
int n;
cin>>n;
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
sum[i]=sum[i-1] + a[i];
}
int j;
for (int i = 1; i <= n; i++)
{
j = i;
while (j>1 && a[j-1]>=a[i])
j=l[j-1];///这是代码的核心 想一下左边的值大于该点的值 那么左边的这个值的左边界
l[i]=j; ///是大于等于左边的值的 所以也一定在该点的区间内 大大减少了复杂度
}
for (int i=n;i;i--)
{
j=i;
while (j<n && a[j+1]>=a[i])
j=r[j+1];///道理同左边的处理
r[i]=j;
}
ll ans = -1;
int ansl = 0, ansr = 0;
for (int i = 1; i <= n; i++)
{
ll v = a[i] * (sum[r[i]] - sum[l[i] - 1]);
if (v > ans)
{
ans = v;
ansl = l[i];
ansr = r[i];
}
}
cout<<ans<<endl<<ansl<<" "<<ansr;
return 0;
}
hdu 1506
这题与上面一题很像 就是给了一个长度为n的序列 代表一段连续的宽为1的长方形的长 问最大连续的长方形的面积是多少 要成长方形 大家想一下木板效应就懂了 这个面积的大小取决于最短的长度 然后向两边拓展区间长度 直到有比他短的就是边界 所以这题与上面一题方法一样
以每个点作为区间最小值 去维护左右两个边界 然后去循环找最大即可 这题比起上一题仅仅是少了一个前缀和处理而已
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100010],l[100010],r[100010];
int main()
{
int n;
while(cin>>n && n)
{
for(int i=1; i <= n; i ++)
scanf("%d",&a[i]);
for (int i=1,j;i<=n;i++)
{
j=i;
while (j>1 && a[j-1]>=a[i])
j=l[j-1];
l[i]=j;
}
for (int i=n,j;i;i--)
{
j=i;
while (j<n && a[j+1]>=a[i])
j=r[j+1];
r[i]=j;
}
ll ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,1LL * a[i] * (r[i] - l[i] + 1));
cout<<ans<<endl;
}
return 0;
}