hdu 区间的价值(RMQ+扫描)

区间的价值

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


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求出所有区间的最大值,然后l[i]表示i左边有几个不小于i的数,r[i]同理。

那么我们可以求出长度为r[i]+l[i]-1的一个区间最小值是i,最大值可以用rmq求出,两者相乘即可得到长度为r[i]+l[i]-1的一个解(此解不一定最优)

我们此时知道长度为n的区间解肯定是最优的(只有一种情况),那么可以用动态规划的思想。

b[i]=max(b[i+1],ans[i]);

ans[i]是已知的长度为i的一个解,那么此时我们还需要求出大于i的所有区间里长度为i的子区间的最大值

b[i+1]则是已知的长度为i+1时的最优解,既然i+1此时已经达到了最优,我们从中取i个数,也一定能取到b[i+1],也就是说,长度为i的区间的值起码会是长度为i+1的区间的值

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define N 100080
int a[N],n;
long long dp[N][20];
int l[N],r[N];
long long ans[N],b[N];
void make_rmq()///建立dp数组,l[i]保存i点左边有几个不小于a[i]的数,r[i]同理
{
    for(int i=0;i<n;i++)
        dp[i][0]=a[i];
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=0;i+(1<<j)-1<n;i++)
        {
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    for(int i=0;i<n;i++)
        l[i]=r[i]=1;
    for(int i=1;i<n;i++)
    {
        for(int j=i-1;j>=0;)
        {
            if(a[j]>=a[i])
                {
                    l[i]+=l[j];
                    j=j-l[j];
                }
            else break;
        }
    }
    for(int i=n-1;i>=0;i--)
    {
        for(int j=i+1;j<n;)
        {
            if(a[j]>=a[i])
                {
                    r[i]+=r[j];
                    j+=r[j];
                }
            else break;
        }
    }
}
long long get_rmq(int s,int t)///获得s-t间最大值
{
    if(s==t) return a[s];
    int k=(int)(log(t-s+1.0)/log(2.0));
    return max(dp[s][k],dp[t-(1<<k)+1][k]);
}
void solve()
{
    memset(ans,0,sizeof(ans));
    for(int i=0;i<n;i++)///ans[i]表示长度为i时候的最大值(不一定是最后的解)
    {
        int ll=l[i],rr=r[i];
        ans[rr+ll-1]=max(ans[rr+ll-1],a[i]*get_rmq(i-ll+1,i+rr-1));
    }
    b[n+1]=-1;
    for(int i=n;i>=1;i--)///比较ans[i]和i+1的最优解,得出最大的
        b[i]=max(b[i+1],ans[i]);
    for(int i=1;i<=n;i++)
        printf("%lld\n",b[i]);
}
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        make_rmq();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值