题目
给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个:
区间中的最小数 * 区间所有数的和最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列 [6 2 1]则根据上述公式, 可得到所有可以选定各个区间的计算值:
[6] = 6 * 6 = 36;
[2] = 2 * 2 = 4;
[1] = 1 * 1 = 1;
[6,2] = 2 * 8 = 16;
[2,1] = 1 * 3 = 3;
[6, 2, 1] = 1 * 9 = 9;
从上述计算可见选定区间 [6] ,计算值为 36, 则程序输出为 36。
区间内的所有数字都在[0, 100]的范围内;
输入描述:
第一行输入数组序列长度n,第二行输入数组序列。
对于 50%的数据, 1 <= n <= 10000;
对于 100%的数据, 1 <= n <= 500000;
输出描述:
输出数组经过计算后的最大值。
输入例子:
8
1 1 4 6 9 8 5 2输出例子:
140
思路:
求子区间中的最小数 * 子区间所有数的和 计算区间中最大值。
暴力想法:假设一个数是某个区间的最小数,向左右两边一直延申,找满足条件的最长的区间。最坏结果是每个数全找,log。
由暴力想法衍生出来的,可减少时间的操作是记录,记录以前算过的而这次不用算了的。记录需要额外空间。
以空间换时间是很常用的手法。
如给出样例,一数字的后续区间若在都是比该数字大的则可在遇见“小的数”的时候才开始计算,因为前面算的都是越来越大。更关键的是把“已计算的合法区间信息”给与”小的数“,这才能减少时间。
单调递增栈,比存在小
单调栈的灵感可能是来源于区间以及单调性(比该数字大,都大)。单调栈,入栈只为记录,出栈在开始真正操作。
一个单调递增栈,栈顶无需考虑下面的栈(都比它小),而下面的栈会享有栈顶带来的额外的区间信息(一定都它大)。
入栈元素若会破坏单调性,说明栈顶(以及其他比入栈元素大)要出栈更新了。
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;
const int _max = 50007;
struct Poi
{
int num; //记录该数字
int region; //记录该数字可用区间(区间内不含本身数字)
}p[_max];
stack<Poi> incrementStack;
int main()
{
int T;
scanf("%d",&T);
for( int i=0; i<T; i++ )
{
scanf("%d",&p[i].num);
p[i].region = 0;
}
p[T].num = p[T].region = 0; //为了最后把栈弹空
int ans = -1;
for( int i=0; i<T+1; i++ )
{
//对于会破坏栈单调性的数据,要不断出栈保证栈的单调性
while( !incrementStack.empty() && incrementStack.top().num >=p[i].num )
{
Poi outStackp = incrementStack.top();
incrementStack.pop();
p[i].region = outStackp.num+outStackp.region+p[i].region; //更新p[i].region
ans = max( ans, outStackp.num * (outStackp.num+outStackp.region+p[i].region) ); //更新ans
}
incrementStack.push(p[i]);
}
printf("%d\n",ans);
return 0;
}
普通的数组记录,比任意小
时间上和单调栈差不多,空间大在了第三个数组。
与单调栈不同,把精力发在区间右测外最小的元素上,一旦找到,整个区间的信息都给它就好了。
#include <cstdio>
#include <algorithm>
using namespace std;
const int _max = 500008;
int num[_max];
int region[_max] ; //该节点合法区间
int region_L[_max]; //最小左节点,记录为避免重复计算
int main()
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
for( int i=0;i<T;i++ )
{
scanf("%d",&num[i]);
region[i] = num[i];
region_L[i] = i;
}
int findSamllerNum = 0;
int ans = -1;
for( int i=0; i<T; i++ )
{
findSamllerNum = i;
while( findSamllerNum<T && num[findSamllerNum+1]>num[i] ) //> 比 >= 好,避免极端数据11111....
{
++findSamllerNum;
region[i] += num[findSamllerNum];
}
findSamllerNum++;
if( region_L[findSamllerNum] > i ) //这里加if避免重复记录
{
region_L[findSamllerNum] = i;
region[findSamllerNum] += region[i] ; //更新SamllerNum region
}
ans = max( ans , region[i] *num[i] ); //更新ans
}
printf("%d\n",ans);
return 0;
}