题意:给定一个数字串,求最小的分段数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;
}