2016"百度之星" - 初赛(Astar Round2B)

区间的价值

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上测试,这两种方法的测试时间完全一样。说明贪心在这题还是很快的。
【加油,加油,加油】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值