秒速五釐米(快速二分跳躍查找答案)

秒速五釐米(快速二分跳躍查找答案)

**秒速五釐米(快速二分跳躍查找答案)
讓我們來看這道題(題目來源:吉首大學)

#問題 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,但是我覺得跳躍式更好一些。

二分## 二分

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