ICPC2021上海區域賽 D.Walker(二分、分類討論)
阿新 • • 發佈:2021-06-14
- 題目:Walker
- 題意:給出兩個點a、b在一條線上的位置,並給出兩個點的速度,問最少需要多少時間可以將整條線段覆蓋(a、b方向任意)
- 解析:
- a或b一人獨自走完線段,兩者最短時間t1
- a、b相向而行,對穿整條線段,兩者最長時間t2
- 二分一個在a、b之間的點x,a負責區間[0, x],b負責區間[x, n]兩者所花費的最大時間t3
- 最後比較t1、t2、t3大小
重點1: 第一種和第二種的情況都挺好想的,主要想總結一下第三種情況,實際上第三種情況包含了非常多的情況,然後這裡二分的思想是希望a與b完成相應區間覆蓋所花的時間儘可能接近,因為最終是要完成整條線段的覆蓋,類似於木桶效應;
重點2: 其次,這裡的二分有點卡精度,eps需要開到1e-7,看其他大佬寫的題解發現,二分迴圈100次精度可以到達1e-30???,這樣不容易被卡精度。
- 程式碼1:
#include<bits/stdc++.h> using namespace std; int t; double n, pa, pb, va, vb; int main() { cin >> t; while(t --) { double res = 1000000000; cin >> n >> pa >> va >> pb >> vb; if(pa > pb) swap(pa, pb), swap(va, vb); //始終讓a在左側 res = min( (n + min(pa, n - pa)) / va, (n + min(pb, n - pb)) / vb); //a、b獨自走完一條線段 res = min(res, max( (n - pa) / va, pb / vb ) ); //a、b相向而行走到邊界 // 二分列舉一個點x, a、b負責覆蓋x的左側、右側 double l = pa, r = pb; while(r - l > 1e-7) { double mid = (l + r) / 2.0; double ta = min(pa + mid, 2 * mid - pa) / va; //先向左到起點後返回mid/先向右到mid再返回起點 double tb = min(n + pb - 2 * mid, 2 * n - pb - mid) / vb; res = min(res, max(ta, tb)); //類似木桶效應 if(ta > tb) r = mid; //縮小a的時間 else l = mid; //增加a的時間 } printf("%.6lf\n", res); } return 0; }
- 程式碼2(控制精度方法):
#include<bits/stdc++.h> using namespace std; int t; double n, pa, pb, va, vb; int main() { cin >> t; while(t --) { double res = 1000000000; cin >> n >> pa >> va >> pb >> vb; if(pa > pb) swap(pa, pb), swap(va, vb); //始終讓a在左側 res = min( (n + min(pa, n - pa)) / va, (n + min(pb, n - pb)) / vb); //a、b獨自走完一條線段 res = min(res, max( (n - pa) / va, pb / vb ) ); //a、b相向而行走到邊界 // 二分列舉一個點x, a、b負責覆蓋x的左側、右側 double l = pa, r = pb; for(int i = 1; i <= 100; i++) { double mid = (l + r) / 2.0; double ta = min(pa + mid, 2 * mid - pa) / va; //先向左到起點後返回mid/先向右到mid再返回起點 double tb = min(n + pb - 2 * mid, 2 * n - pb - mid) / vb; res = min(res, max(ta, tb)); //類似木桶效應 if(ta > tb) r = mid; //縮小a的時間 else l = mid; //增加a的時間 } printf("%.6lf\n", res); } return 0; }