您的位置:首页 > 其它

hdu 3486(RMQ+高效枚举)

2016-05-05 20:04 204 查看
题意:把n分为 均分为m 段 每段n/m个数字  每段可以选一个最大的数 求这些数相加起来>k的最小的m

解题思路:这道题本来想用二分+rmq的,但discuss里面说二分是错的,所以只能另想别的办法了。

首先是可以肯定的,一定要枚举段数,接下来就是如何减少对内层循环的枚举次数。

当分成i段得到的段长L1
与 之前分成 i-1段得到的段长相同  那就只要把前一次的结果再加上这一次 第i段的最大值  就是当前的结果了   这一步少做了很多步骤 节约了很多时间 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

const int maxn = 200005;
int n,m,a[maxn],dp[maxn][20];

void init()   // 从点i开始   长1<<j     的最大值
{
for(int i = 1; i <= n; i++)
dp[i][0] = a[i];
for(int j = 1; (1<<j) <= n; j++)
for(int i = 1; i + (1<<j) - 1 <= n; i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int rmq(int l,int r)
{
int k = (int)(log(r - l + 1.0) / log(2.0));
return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

int solve()
{
int prev = -1,sum,j,left,right;
for(int i = 1; i <= n; i++)//段数
{
int l = n / i;
if(prev != l)
{
j = 1; sum = 0;
}
while(j <= i)
{
left = (j-1) * l + 1;
right = j*l;
sum += rmq(left,right);
if(sum > m)
return i;
j++;
}
prev = l;
}
return -1;
}

int main()
{
while(scanf("%d%d",&n,&m),n != -1,m != -1)
{
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
init();
printf("%d\n",solve());
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  枚举 RMQ