秒速五厘米(快速二分跳跃查找答案)

秒速五厘米(快速二分跳跃查找答案)

**秒速五厘米(快速二分跳跃查找答案)
让我们来看这道题(题目来源:吉首大学)

#问题 D: 秒速五厘米
描述
樱花飘落的速度,每秒五厘米。
动漫《秒速五厘米》中,明里曾在信中写道“我家附近有棵很大的樱花树,到了春天,
那棵树上的花瓣,大概也会以每秒五公分的速度飘落,而我则在想,要是能和贵树一
起迎接春天的来临该有多好啊。”
来年春天,他们没能像约定那样一起迎接春天的到来,看樱花飘落。但有一个魔法,
可以让他们有m秒的时间一起看樱花飘落,樱花树上有n朵樱花,每朵樱花都有一个高度a[i],
樱花飘落的速度为v,樱花只能一朵一朵的飘落,如果某朵樱花飘落的时间不是整数,
则那朵樱花飘落所需的时间要向上取整,即,若两朵樱花的高度都为7,飘落的速度为2,
则两朵樱花飘落的时间为8,现在你可以控制樱花飘落的速度v,当v为何值时才能使所有的
樱花在m秒的时间内全部飘落且v的值要尽可能小。
格式
输入格式
第一行输入两个正整数n和m(1<=n<=m<=2000000)
第二行输入n个正整数a[i](1<=a[i]<=10000000),分别指的是每朵樱花的高度
输出格式
输出一个正整数v,代表樱花最合适的速度
样例
样例输入 Copy
2 10
5 6
样例输出 Copy
2
题解:该题数据过于庞大,不可能通过直接枚举得到,所以需要二分答案法;
但是测试时发现二分也超时了,最后我想出了两种方法。

第一种代码如下`

#include<bits/stdc++.h>
using namespace std;
long long b[10000010];
long n,m,maf;
bool check(long x)
{
	long i;
	long long sum=0,as=0; 
	long ans=0; 
	for(i=1;;i++)
	{
		if(i!=1)
		{
			sum=b[i*x-x];  //对于每个x,直接查找 i*x 到(i+1)*x之间的路程数
			as=b[i*x];     // 从而实现跳跃式的遍历
		}
		if(i*x>=maf)
		{
			ans+=(b[maf]-sum)*i;
			break;
		}
		if(i==1) ans+=b[x];
		if(i!=1)ans+=(as-sum)*i;
	}
	if(ans>m) return false;
	else return true; 
}
int main()
{
	while(scanf("%ld %ld",&n,&m)!=EOF)
	{
		long long sum=0,mid,ans;
		for(long i=1;i<=n;i++)
		{
			long f;
		    scanf("%ld",&f);
		    if(i==1) maf=f;
		    else if(i!=1 && maf<f) maf=f;   // **找到最大的数,缩小桶的区间**
		    b[f]++;                        // **桶装数**
		    sum+=f;
		}
		for(long i=2;i<=maf;i++)
		b[i]+=b[i-1];              // **通过这步将每个b[i] 变为 小于等于i 的 路程个数之和**
		long r=sum,l=1; 
		while(r>=l)            // **普通二分 没什么变化 重点在这个查找函数里面**
		{
			mid=(l+r)/2;
			if(check(mid))
			{
				ans=mid;
				r=mid-1;
			}
			else l=mid+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

第二种方法

第二种方法的效率比第一种要低一些,重点在対二分剪枝

long r=maf,l=sum/m;    // **剪枝**
	while(r>=l) 
	{
		mid=(l+r)/2;
		if(check(mid))
		{
			ans=mid;
			r=mid-1;
		}
		else l=mid+1;
	}

如果使用剪枝的话,可以直接遍历每一个数就行,不需要跳跃式遍历也可以AC,但是我觉得跳跃式更好一些。

二分## 二分

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章