hdu5696-分治-暴力剪枝-rmq-dfs-单调栈-区间的价值

12 篇文章 0 订阅
4 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=5696
后几个写法慢慢写。
开始写了一个暴力,一直tle,应该是边界没处理好。尴尬。。
我们可以确定一种情况。
除却相等使用的情况。最小的那个数作为区间中的最小数,越少越好,而最大数,越多越好
两个贪心策略:尽可能的用最大这个数。(枚举区间情况)
2 同一个 最大*最小 可以被很多长度区间所利用(包含区间情况)
。暴力 虽然是枚举的最大值,但是这个最大值是可以被好几个 区间所利用的,。所以可以。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=100050;
ll dp[maxn];
ll a[maxn];
int main()
{   int m;
    while(~scanf("%d",&m)){
        for(int i=1;i<=m;i++)
             scanf("%lld",&a[i]);
           memset(dp,0,sizeof(dp));
           for(int i=1;i<=m;i++){
              int l=i;
              int r=i;
              dp[1]=max(dp[1],a[i]*a[i]);
             ll min1=a[i];
              while(1){
                     if(a[l]>a[i]||a[r]>a[i]) break;
                 if((r-l+1)==m) break;
                 if((a[l-1]>a[r+1]||(r==m))&&1!=l)
                      {min1=min(a[l-1],min1);l--;}
                      else
                      {min1=min(a[r+1],min1);r++;}
                    dp[r-l+1]=max(dp[r-l+1],a[i]*min1);

              }
           }

           for(int i=1;i<=m;i++)
              printf("%lld\n",dp[i]);
   }

    return 0;
}

但是单调栈我就要说一下了
这个是用的单调栈+rmq(st算法倍增)来弄得。
特别注明一下 单调栈,
维护一个单调递增的栈,用来确定i为最小来确定的区间,这个可以当做模板使用。可以自己想一下。
先维护每个i为最小的区间,然后在这个区间内用rmq求极大值,相乘就行了,注意 开头我说的第二项原理,并且第二项原理是可以从1用到m的。自己一下就明白了。
那为什么这个题不用枚举每一个为最大值,然后rmq求极小值,然后在正常搞呢。
我试着写了一下,mb竟然不对。大概是因为 最大值只能利用一次。
(恩,最大值被多次利用有两种情况,一种是第一种计算区间的时候,第二种是区间包含的情况。)
我说的不太明白,慢慢我会补几张图的,

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include <bits/stdc++.h>
typedef long long ll;
/*我们可以确定一种情况。
除却相等使用的情况。最小的那个数作为区间中的最小数,越少越好,而最大数,越多越好
两个贪心策略:尽可能的用最大这个数。
2 同一个 最大*最小 可以被很多长度区间所利用
*/
using namespace std;
const int maxn=100005;
int l[maxn];
int r[maxn];
stack<pair<int,ll> >q;
ll a[maxn];
ll FMax[maxn][20];
int m;
ll dp[maxn];
void Init(){
    int i,j;
    for(i=1;i<=m;i++)
       FMax[i][0]=a[i];//以i为起点,长度为1的区间,初始值为a[i];
    for(i=1;(1<<i)<=m;i++){   //区间慢慢扩增
        for(j=1;j+(1<<i)-1<=m;j++){   //区间起点,
           // FMin[j][i]=min(FMin[j][i-1],FMin[j+(1<<(i-1))][i-1]);
            FMax[j][i]=max(FMax[j][i-1],FMax[j+(1<<(i-1))][i-1]);
        }
    }
}
ll Query(int l,int r){
    int k=(int)(log(r-l+1)/log(2));
    return max(FMax[l][k],FMax[r-(1<<k)+1][k]);//0(1)的查询
}
int main(){
    while(~scanf("%d",&m)){
         memset(dp,0,sizeof(dp));
         memset(FMax,0,sizeof(FMax));
         memset(a,0,sizeof(a));
         memset(l,0,sizeof(l));
         memset(r,0,sizeof(r));
         for(int i=1;i<=m;i++){
             scanf("%lld",&a[i]);
         }
         while(!q.empty())
             q.pop();
         q.push(make_pair(0,0));
          for(int i=1;i<=m;i++){
              while(!q.empty()){
                   pair<int,ll> u=q.top();
                   if(u.second>a[i]){
                      r[u.first]=i-1;
                      q.pop();
                   }
                   else { q.push(make_pair(i,a[i]));
                          break;
                   }
              }
         }
         while(!q.empty()){
             pair<int,ll>u=q.top();
             r[u.first]=m;
             q.pop();
         }
          q.push(make_pair(0,0));
          for(int i=m;i>=1;i--){
              while(!q.empty()){
                   pair<int,ll> u=q.top();
                   if(u.second>a[i]){
                      l[u.first]=i+1;
                      q.pop();
                   }
                   else { q.push(make_pair(i,a[i]));
                          break;
                   }
              }
         }
         while(!q.empty()){
             pair<int,ll>u=q.top();
             l[u.first]=1;
             q.pop();
         }
         /*for(int i=1;i<=m;i++){
            printf("%d  %d\n",l[i],r[i]);
         }*/
         Init();
         for(int i=1;i<=m;i++){
            int len=r[i]-l[i]+1;
            dp[len]=max(dp[len],Query(l[i],r[i])*a[i]);
         }
         dp[m+1]=0;
         ll ans=0;
         for(int i=m;i>=1;i--){
            ans=max(dp[i+1],ans);
            dp[i]=max(dp[i],ans);
         }
         for(int i=1;i<=m;i++){
             printf("%lld\n",dp[i]);
         }
    }


    return 0;
}

最后的写法是 分治。有空多看

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#define LL long long  
using namespace std;  
const int MAXN = 1e5+5;  
LL a[MAXN],temp[MAXN],ans[MAXN];  
void solve(int l,int r)  
{  
    if(l>r)  
        return ;  
    int p=0;  
    for(int i = l;i<=r;i++)  
    {  
        if(!p||a[i]<a[p])  
            p = i;  
    }  
    int w = r-l+1;  
    for(int i = 1;i<=w;i++)  
        temp[i] = 0;  
    LL pre = 0;  
    //遍历一遍区间获得他们的最大区间值  
    for(int i = p-1;i>=l;i--)  
    {  
        if(temp[p-i+1]<max(pre,(LL)a[p]*a[i]))  
        {  
            temp[p-i+1] = max(pre,(LL)a[p]*a[i]);  
            //因为最小值固定下来了,也就是看最大值哪个大  
            //短区间的值可以给长区间做更新(也就是短区间的最大值更大)  
            pre=temp[p-i+1];  
        }  
    }  
    pre = 0;  
    for(int i = p+1;i<=r;i++)  
    {  
        if(temp[i-p+1]<max(pre,(LL)a[p]*a[i]))  
        {  
            temp[i-p+1] = max(pre,(LL)a[p]*a[i]);  
            pre = temp[i-p+1];  
        }  
    }  
    ans[1] = max(ans[1],a[p]*a[p]);  
    pre = 0;  
    //整体的区间长度的最大价值更新操作  
    for(int i = 2;i<=w;i++)  
    {  
        if(ans[i]<max(pre,temp[i]))  
        {  
            ans[i]=max(pre,temp[i]);  
        }  
        pre = max(pre,temp[i]);  
    }  
    solve(l,p-1);  
    solve(p+1,r);  
}  
int main()  
{  
    freopen("in2.txt","r",stdin);  
    //freopen("out1.txt","w",stdout);  
    int n;  
    while(~scanf("%d",&n))  
    {  
        memset(ans,0,sizeof(ans));  
        for(int i = 0;i<MAXN;i++)  
            temp[i] = 0;  
        for(int i =1;i<=n;i++)  
            scanf("%I64d",&a[i]);  
        solve(1,n);  
        for(int i = 1;i<=n;i++)  
        {  
            printf("%I64d\n",ans[i]);  
        }  
    }  
    return 0;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值