1. 程式人生 > 實用技巧 >【解題報告】 洛谷P1542 包裹快遞

【解題報告】 洛谷P1542 包裹快遞

【解題報告】 洛谷P1542 包裹快遞

題目描述

小K成功地破解了密文。但是乘車到X國的時候,發現錢包被偷了,於是無奈之下只好作快遞員來攢足路費去Orz教主……

一個快遞公司要將n個包裹分別送到n個地方,並分配給郵遞員小K一個事先設定好的路線,小K需要開車按照路線給的地點順序相繼送達,且不能遺漏一個地點。小K得到每個地方可以簽收的時間段,並且也知道路線中一個地方到下一個地方的距離。若到達某一個地方的時間早於可以簽收的時間段,則必須在這個地方停留至可以簽收,但不能晚於簽收的時間段,可以認為簽收的過程是瞬間完成的。

為了節省燃料,小K希望在全部送達的情況下,車的最大速度越小越好,就找到了你給他設計一種方案,並求出車的最大速度最小是多少。

輸入格式

第1行為一個正整數n,表示需要運送包裹的地點數。

下面n行,第i+1行有3個正整數xi,yi,si,表示按路線順序給出第i個地點簽收包裹的時間段為[xi, yi],即最早為距出發時刻xi,最晚為距出發時刻yi,從前一個地點到達第i個地點距離為si,且保證路線中xi遞增。

可以認為s1為出發的地方到第1個地點的距離,且出發時刻為0。

輸出格式

僅包括一個正數,為車的最大速度最小值,結果保留兩位小數。

輸入輸出樣例

輸入 #1

3
1 2 2
6 6 2
7 8 4

輸出 #1

2.00

說明/提示

對於20%的資料,\(n \le 10\)

對於30%的資料\(x_i,y_i,x_i\le 1000\)

對於50%的資料,\(n\leq1000\)

對於100%的資料,\(n\le 200000,x_i\le y_i\le 10^8,s_i \le 10^7\)

時限1s

第一段用1的速度在時間2到達第1個地點,第二段用0.5的速度在時間6到達第2個地點,第三段用2的速度在時間8到達第3個地點。

思路

二分

看到這道題,有一個關鍵詞,最大速度最小,這樣我們就像想到了二分的方法,並且速度越大,當然可以所有都送達,所以這裡要找的是一個臨界點的問題符合單調性,所以更加確定的使用了二分的方法。

大概思路就是設定一個check函式,引數為這個最大速度的最小\(V_{min}\)然後對於時間進行模擬,如果在某個包裹要求的時間之前達到了這個包裹的地點(將原來的總時間加上路程/速度),當然就是要等到這個時間,直接把總時間\(a\)

變成這個地點的包裹時間,如果總時間\(a\)大於這個包裹的末時間,也就是說這個包裹沒有送成功,當然是不符合答案的,所以直接返回false,在最後,如果所有的包裹都能送達,返回一個true,表示這個最小的速度肯定是小於等於當前的速度mid的,所以計算左區間有沒有答案,然後如果返回的是false的話,說明這個mid還不夠大,連包裹都不能在規定的時間內送到,當然速度要更大,所以計算右區間,這樣就能找到一個剛剛好的值使最大速度最小。

這道題還需要注意的是double的精度只有16位(包括小數),如果整數部分過大的話將會捨棄小數部分的精度,所以我們要使用long double ,在輸出的時候要用 %Lf這個L一定要是大寫的,否則輸出的還是double精度的數字。

程式碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int MAXN=2000005;
const double eps=0.00001;
int n;
int x[MAXN],y[MAXN],s[MAXN];
long double ans;
bool check(double vmin)
{
	long double a=0;//a表示已經進行的時間 
	for(int i=1;i<=n;i++)
	{
		a+=s[i]/vmin;
		if(a<x[i])
		a=x[i];
		if(a>y[i])
		return false;
	}
	return true;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>x[i]>>y[i]>>s[i];
	long double l=0,r=1e9;
	while(r-l>=eps)
	{
		double mid=(l+r)/2;
		if(check(mid)) r=mid,ans=mid;
		else l=mid;
	}
	printf("%.2Lf\n",ans);
	return 0;
}