中石油训练赛 - High Load Database(二分+记忆化)

题目链接:点击查看

题目大意:给出一个长度为 n 的数列,再给出 m 次询问,每次询问给出一个阈值 x ,问最少将数列分割成多少段,可以使得每一段的总和都不超过 x,无解的话输出 Impossible

题目分析:首先单独讨论一下无解的情况,因为数列总是可以被分成 n 段,也就是每个元素独立一段,这样的话如果阈值小于最大值的话显然是无解的

如果不考虑时间复杂度的话,不难想到一个 n * m 的做法,就是对于每个询问,都扫一遍数列,然后贪心求解

考虑优化,首先 m 个询问肯定是无法优化的,对于扫一遍数列的时间复杂度 O( n ),考虑优化,因为数列中的每个元素都是正整数,所以维护的前缀和一定是递增的,所以对于每段区间可以进行二分去划分,比如初始时未经过划分的总区间为 [ 1 , n ] ,设阈值为 x ,我们可以二分出一个最大的 pos,使得 sum[ pos ] - sum[ 0 ] <= x,这样可以直接将区间 [ 1 , pos ] 划分为第一段,然后再去给区间 [ pos + 1 , n ] 进行划分,这样做的时间复杂度最坏还是会退化为 O( n ) 的,但考虑利用数组记忆化一下,因为题目中约束了每个询问的阈值一定是小于等于 1e6 的,好像也是在暗示我们要记忆化一样,然后时间复杂度就不太会计算了,暂且当做 O( 能过 ) 吧

代码:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e6+100;

int n,m,mmax,sum[N],ans[N];

int solve(int x)
{
	if(ans[x])
		return ans[x];
	int last=1;
	ans[x]=0;
	while(last<=n)
	{
		ans[x]++;
		int l=last,r=n,ans=-1;
		while(l<=r)
		{
			int mid=l+r>>1;
			if(sum[mid]-sum[last-1]<=x)
			{
				l=mid+1;
				ans=mid;
			}
			else
				r=mid-1;
		}
		last=ans+1;
	}
	return ans[x];
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		int num;
		scanf("%d",&num);
		mmax=max(mmax,num);
		sum[i]=sum[i-1]+num;
	}
	scanf("%d",&m);
	while(m--)
	{
		int x;
		scanf("%d",&x);
		if(x<mmax)
			puts("Impossible");
		else
			printf("%d\n",solve(x));
	}


















   return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/109003936
今日推荐