zoukankan      html  css  js  c++  java
  • hdu3486 Interviewe (二分+线段树求区间最值)

    题意:给定一个数字串,求最小的分段数m(每段个数按数字窜顺序切割,余数丢弃)
    求各个段中的最大值之和超过k的最小m是多少

    分析:先求出m的上界和下界,二分枚举m

    之后就是一个区间求最值的问题了,还不会RMQ,只能用线段树求了

    线段树版
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define MAXN 200000+10
    using namespace std;
    struct node
    {
    int l,r,maxn;
    }p[MAXN<<2];
    int a[MAXN],n;
    void bulid(int s,int t,int k)
    {
    p[k].l=s;
    p[k].r=t;
    if(s==t)
    {
    p[k].maxn=a[s];
    return ;
    }
    int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
    bulid(s,mid,kl);
    bulid(mid+1,t,kr);
    p[k].maxn=max(p[kl].maxn,p[kr].maxn);
    }
    int query(int k,int l,int r)
    {
    if(p[k].l>r||p[k].r<l)
    return 0;
    if(p[k].l>=l&&p[k].r<=r)
    return p[k].maxn;
    int kl=k<<1,kr=kl+1;
    return max(query(kl,l,r),query(kr,l,r));
    }
    int get(int m)//求出分成m段的情况下,最大能力值是多少
    {
    int len=n/m,sum=0;
    for(int i=1;i<=m;i++)
    sum+=query(1,len*(i-1)+1,len*i);
    return sum;
    }
    int main()
    {
    int k,sum,ans;
    while(scanf("%d %d",&n,&k)==2)
    {
    if(n<0 && k<0)
    break;
    int maxn=-1,minn=INT_MAX,sum=0;
    for(int i=1;i<=n;i++)
    {
    scanf("%d",&a[i]);
    sum+=a[i];
    if(a[i]>maxn)
    maxn=a[i];
    if(a[i]<minn)
    minn=a[i];
    }
    minn=minn?minn:1;
    maxn=maxn?maxn:1;
    if(maxn>=k) {//若最大的值大于k,则分成一段
    printf("1\n");
    continue;
    }
    if(sum<k){//若所有总和都小于k,无解
    printf("-1\n");
    continue;
    }
    bulid(1,n,1);
    int r=min((k/minn+1),n);
    int l=k/maxn;
    if(l==0) l++;
    while(l<=r)//二分枚举段数
    {
    int mid=(l+r)>>1;
    int t=get(mid);
    if(t>k)
    {
    r=mid-1;
    ans=mid;
    }
    else l=mid+1;
    }
    printf("%d\n",ans);
    }
    return 0;
    }

    特地学了RMQ,这个博客写的好详细呀http://www.cnblogs.com/cnjy/archive/2009/08/30/1556566.html

    可是为什么这道题,我的RMQ版跟线段树版效率上没什么差别

    View Code
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<math.h>
    #define MAXN 200000+10
    using namespace std;
    int dp[MAXN][20];
    int a[MAXN],n;
    void init()
    {
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
    dp[i][0]=a[i];
    for(int j=1;j<=log((double)(n+1))/log(2.0);j++)
    {
    int maxx=n+1-(1<<j);
    for(int i=1;i<=maxx;i++)
    dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    }
    }
    int query(int a,int b)
    {
    int k=(int)(log((double)(b-a+1))/log(2.0));
    return max(dp[a][k],dp[b-(1<<k)+1][k]);
    }
    int get(int m)//求出分成m段的情况下,最大能力值是多少
    {
    int len=n/m,sum=0;
    for(int i=1;i<=m;i++)
    sum+=query(len*(i-1)+1,len*i);
    return sum;
    }
    int main()
    {
    int k,sum,ans;
    while(scanf("%d %d",&n,&k)==2)
    {
    if(n<0 && k<0)
    break;
    int maxn=-1,minn=INT_MAX,sum=0;
    for(int i=1;i<=n;i++)
    {
    scanf("%d",&a[i]);
    sum+=a[i];
    if(a[i]>maxn)
    maxn=a[i];
    if(a[i]<minn)
    minn=a[i];
    }
    minn=minn?minn:1;
    maxn=maxn?maxn:1;
    if(maxn>=k) {//若最大的值大于k,则分成一段
    printf("1\n");
    continue;
    }
    if(sum<k){//若所有总和都小于k,无解
    printf("-1\n");
    continue;
    }
    init();
    int r=min((k/minn+1),n);
    int l=k/maxn;
    if(l==0) l++;
    while(l<=r)//二分枚举段数
    {
    int mid=(l+r)>>1;
    int t=get(mid);
    if(t>k)
    {
    r=mid-1;
    ans=mid;
    }
    else l=mid+1;
    }
    printf("%d\n",ans);
    }
    return 0;
    }



  • 相关阅读:
    图片加载框架之Glide和Picasso
    图片加载框架之fresco
    图片加载框架之ImageLoader
    依赖注入框架之androidannotations
    依赖注入框架之butterknife
    Android依赖注入框架之Dagger2
    EvenBus源码分析
    时间总线框架之EvenBus
    数据库开源框架之litepal
    数据库开源框架之sqlcipher加密数据库
  • 原文地址:https://www.cnblogs.com/nanke/p/2354556.html
Copyright © 2011-2022 走看看