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

本文介绍了一个关于区间价值的问题,探讨了两种解决方案:一种利用RMQ和线段树维护区间最大和最小值,另一种采用贪心策略。通过实例分析,详细解释了如何找到不同区间长度下的最大区间价值。

区间的价值

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.youkuaiyun.com/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上测试,这两种方法的测试时间完全一样。说明贪心在这题还是很快的。
【加油,加油,加油】
已经博主授权,源码转载自 https://pan.quark.cn/s/a4b39357ea24 QueueForMcu 基于单片机实现的队列功能模块,主要用于8位、16位、32位非运行RTOS的单片机应用,兼容大多数单片机平台。 开源代码:https://.com/xiaoxinpro/QueueForMcu 一、特性 动态创建队列对象 动态设置队列数据缓冲区 静态指定队列元素数据长度 采用值传递的方式保存队列数据 二、快速使用 三、配置说明 目前QueueForMcu只有一个静态配置项,具体如下: 在文件 中有一个宏定义 用于指定队列元素的数据长度,默认是 ,可以根据需要更改为其他数据类型。 四、数据结构 队列的数据结构为 用于保存队列的状态,源码如下: 其中 为配置项中自定义的数据类型。 五、创建队列 1、创建队列缓存 由于我们采用值传递的方式保存队列数据,因此我们在创建队列前要手动创建一个队列缓存区,用于存放队列数据。 以上代码即创建一个大小为 的队列缓存区。 2、创建队列结构 接下来使用 创建队列结构,用于保存队列的状态: 3、初始化队列 准备好队列缓存和队列结构后调用 函数来创建队列,该函数原型如下: 参数说明: 参考代码: 六、压入队列 1、单数据压入 将数据压入队列尾部使用 函数,该函数原型如下: 参数说明: 返回值说明: 该函数会返回一个 枚举数据类型,返回值会根据队列状态返回以下几个值: 参考代码: 2、多数据压入 若需要将多个数据(数组)压入队列可以使用 函数,原理上循环调用 函数来实现的,函数原型如下: 参数说明: 当数组长度大于队列剩余长度时,数组多余的数据将被忽略。 返回值说明: 该函数将返回实际被压入到队列中的数据长度。 当队列中的剩余长度富余...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值