区间的价值

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 685    Accepted Submission(s): 343


Problem Description
我们定义“区间的价值”为一段区间的最大值*最小值。

一个区间左端点在 L,右端点在 R,那么该区间的长度为 (RL+1)

现在聪明的杰西想要知道,对于长度为 k的区间,最大价值的区间价值是多少。

当然,由于这个问题过于简单。

我们肯定得加强一下。

我们想要知道的是,对于长度为 1n的区间,最大价值的区间价值分别是多少。

样例解释:

长度为 1的最优区间为 22 答案为 66

长度为 2的最优区间为 45 答案为 44

长度为 3的最优区间为 24 答案为 26

长度为 4的最优区间为 25 答案为 26

长度为5的最优区间为 15 答案为 16
 

Input
多组测试数据

第一行一个数 n(1n100000)

第二行 n个正整数 (1ai109),下标从 1开始。

由于某种不可抗力, ai的值将会是 1109内<b style="color:red;">随机产生</b>的一个数。(除了样例)
 

Output
输出共 n行,第 i行表示区间长度为 i的区间中最大的区间价值。
 

Sample Input
5 1 6 2 4 4
 

Sample Output
36 16 12 12 6 【题意】中文题面。 【分析&解题思路】这道题上来看到感觉很不好做啊。要枚举所有的区间维护区间的最大值和最小值显然是不合理的。那么这道题该用什么办法呢?维护区间最大值和最小值可 以用RMQ或者线段树来完成。然后枚举每一个点为最小值,然后还要维护一个东西。那就是每个点可以左右延伸的最大范围,然后当前答案为ans[r-l+1]=max(ans[r-l+1], RMQ_MAX(l,r)*A[i]);然后维护出来的并不是最后的答案,想一下为什么?假设当前的ans是一个区间长度curlen的最佳答案,那么区间长度<curlen的答案肯定是>=ans 的。所以这里逆序递推就可以得到答案了。这里说得不清楚的话,我举个例子,比如1,2,3,4长度为3的最佳答案肯定是8,那么长度为2的答案是12>8,1,3,2,4也是如此。 因为我们定了一个最大值之后,只需要找一个最小值最大的值就行了,而长度越短,松弛空间更加的大! 【PS这里贴一份别人用这种方法做的AC代码】 【来源:http://blog.csdn.net/queuelovestack/article/details/51476288】 【代码】
const int N = 100005;  
const int M = 40;  
const int inf = 100000000;  
const int mod = 2009;  
int s[N],n,maxnum[N][20],l[N],r[N];  
__int64 ans[N];  
void RMQ()          //预处理  O(nlogn)  
{  
    int i,j;  
    int m=(int)(log(n*1.0)/log(2.0));  
    for(i=1;i<=n;i++)  
        maxnum[i][0]=s[i];  
    for(j=1;j<=m;j++)  
        for(i=1;i+(1<<j)-1<=n;i++)  
            maxnum[i][j]=max(maxnum[i][j-1],maxnum[i+(1<<(j-1))][j-1]);  
}  
int Ask_MAX (int a,int b)   //O(1)  
{  
    int k=int(log(b-a+1.0)/log(2.0));  
    return max(maxnum[a][k],maxnum[b-(1<<k)+1][k]);  
}  
int main()  
{  
    int i,k;  
    while(~scanf("%d",&n))  
    {  
        memset(ans,0,sizeof(ans));  
        for(i=1;i<=n;i++)  
        {  
            scanf("%d",&s[i]);  
            l[i]=r[i]=i;  
        }  
        RMQ();  
        for(i=2;i<=n;i++)  
        {  
            k=i-1;  
            while(s[i]<=s[k])  
                k=l[k]-1;  
            l[i]=k+1;  
        }  
        for(i=n-1;i>0;i--)  
        {  
            k=i+1;  
            while(s[i]<=s[k])  
                k=r[k]+1;  
            r[i]=k-1;  
        }  
        for(i=1;i<=n;i++)  
            ans[r[i]-l[i]+1]=max(ans[r[i]-l[i]+1],(__int64)Ask_MAX(l[i],r[i])*s[i]);  
        for(i=n-1;i>0;i--)  
            ans[i]=max(ans[i+1],ans[i]);  
        for(i=1;i<=n;i++)  
            printf("%I64d\n",ans[i]);  
    }  
    return 0;  
}  

【解法二】这题还有一种更加奇妙的解法,贪心!我在第一种解法中已经提到过了,如果枚举当前的值为最大值的话,那么我们选择最小值,要让最小值最小的话,就可以得到
更优秀的答案。
【AC代码】
//Cloud , you are my sunshine!
//I can't live without you!
//You are the most beautiful girl in the world!
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005;
int a[maxn];
LL ans[maxn];
int n;
int main(){
    while(~scanf("%d",&n)){
        memset(ans,0,sizeof(ans));
        for(int i=1; i<=n; i++) scanf("%d",&a[i]);
        LL maxx,minn;
        for(int i=1; i<=n; i++){//枚举最大值,贪心取次大
            int l=i,r=i,curlen=1;
            maxx=minn=a[i];
            while(1){
                ans[curlen]=max(ans[curlen],maxx*minn);
                //只能向右扩展
                if(l==1||a[l-1]>maxx){
                    if(a[r+1]>maxx) break;
                    else if(r==n) break;
                    else{
                        r++;
                        curlen++;
                        if(a[r]<minn) minn=a[r];
                    }
                }
                //可以向右也可以向左扩展
                else{
                    if(r==n||a[r+1]>maxx){//只能向左扩展
                        l--;
                        curlen++;
                        if(a[l]<minn) minn=a[l];
                    }
                    else{
                        if(a[l-1]>a[r+1]){
                            l--;
                            curlen++;
                            if(a[l]<minn) minn=a[l];
                        }
                        else
                        {
                            r++;
                            curlen++;
                            if(a[r]<minn) minn=a[r];
                        }
                    }
                }
            }
        }
        for(int i=1; i<=n; i++){
            printf("%I64d\n",ans[i]);
        }
    }
    return 0;
}

【补充】经过我在hdoj上测试,这两种方法的测试时间完全一样。说明贪心在这题还是很快的。
【加油,加油,加油】