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

来源:互联网 发布:esxi添加网络文件系统 编辑:程序博客网 时间:2024/06/06 07:41

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;  }
原创粉丝点击