有些东西不记下,就会忘记.

POJ2796

 题目意思就是给你一个序列,让你求其中一个子序列使得这个子序列和乘以这个子序列中最小值后最大。最初以为要用什么RMQ,还单独去查了下这方面的资料,后来看到了牛人写的这个算法才知道是这么的简单!

算法:
用数组v记录元素值,第i个元素为v[i]。对每个元素求出一个子序列使得这个序列最长,且这个元素是这个序列的最小值。这一步的算法有点技巧,就是对每个元素先求这个序列的开始位置用s[i]记录第i个元素的开始位置。对于元素j,令k = j - 1, 比较v[j], v[k]如果v[j] > v[k] 则 s[j] = s[k] + 1;否则k = s[k] - 1,因为否则意味v[j] <= v[k],那么s[k]到k的元素都大于等于v[k]也就大于等于v[j]不需要重新遍历。对序列的结束位置用数组e记录,用此算法一样得到,就是倒过来遍历一边。复杂度在O(n)到O(n^2)之间。
用一个数组sum[i]记录从第一个元素1到i之间的和,有sum[i] = sum[i-1] + v[i]这一步复杂度O(n)。
从1到n遍历 v[i]*(sum[e[i]] - sum[s[i] - 1])最大值即可,复杂度O(n)

总体复杂度超过O(n)但远低于O(n^2)。

#include<stdio.h>
int d[100002];
int s[100002],e[100002];
__int64 sum[100002];
int main()
{
int n;
while(scanf("%d",&n)==1)
{
   int i,j,p,q;
   __int64 max=-1;
   d[0]=-1;
        s[0]=1;
   sum[0]=0;
   d[n+1]=-1;
   for(i=1;i<=n;i++)
   {
    scanf("%d",&d[i]);
             j=i-1;
    while(j>=0)//d[i]>d[i-1] : s[i]=i        d[i]<=d[i-1]:
    {  
     if(d[i]>d[j])
     {
      s[i]=j+1;
      break;
     }
     else
     j=s[j]-1;
    }
             sum[i]=sum[i-1]+d[i];
   }
   for(i=n;i>0;i--)
   {
    j=i+1;
    while(j<=n+1)
    {
     if(d[i]>d[j])
     {
      e[i]=j-1;
                    break;
     }
     else
      j=e[j]+1;
    }
   }
         for(i=1;i<=n;i++)
   {
            if ( (sum[e[i]]-sum[s[i]-1])*d[i]>max)
    {
     max=(sum[e[i]]-sum[s[i]-1])*d[i];
     p=s[i];
     q=e[i];
    }
   }
   printf("%I64d\n",max);
   printf("%d %d\n",p,q);
}
return 0;
}

posted on 2010-05-19 17:04  youngxiao  阅读(644)  评论(0)    收藏  举报

导航