備戰Noip2018模擬賽15(A組) T2 Column 淘淘的柱子朋友
阿新 • • 發佈:2018-12-18
10月17日備戰Noip2018模擬賽15(A組)
T2 Column淘淘的柱子朋友
題目描述
明天就是noip了,淘淘家來了很多柱子朋友,他們有粗有細,但都躺在淘淘家不動,這叫淘淘很是不爽,因為他需要空間來思考人生。但是他又不好意思把朋友們趕走,也不想把它們摞起來,於是他來想你求助。
你需要解決這樣一個問題,地上從左到右平行地倒放了n根柱子,各自有一個半徑ri,你需要用兩個板子從兩邊向中間推,將柱子們儘量地向中間壓起來,但是不能讓柱子離地。請告訴淘淘柱子的最少佔地範圍,即兩個板子之間最小距離。
輸入格式
第一行為柱子數n
第二行包含n個整數r1,r2,...,rn(1≤ri≤100)代表從左到右柱子的半徑。
輸出格式
輸出一行一個保留三位的小數,表示答案。
輸入樣例
2
4 12
輸出樣例
29.856
資料範圍
對於10%的資料,n≤5;
對於60%的資料,n≤100;
對於100%的資料,n≤1000。
思路
DP
EMM ......這竟然是一道純數學題, 死算
既然要距離最小,那麼每個柱子一定要與左右的柱子或者板子相切
兩個圓之間的水平距離為,如下圖所示
除此之外還有一種特殊情況,有一個半徑很小的圓柱被夾在兩個直徑較大的圓柱中間,如下圖所示
這樣我們就應該直接取更大的值
狀態設計: dp [i]表示從第一個圓柱到第i個圓柱的距離
狀態轉移:
初始化:
複雜度:
程式碼
#include <iostream> #include <cstdio> #include <cmath> using namespace std; const int N = 1001; const double eps = 1e-12; int n; double ans, h; double r[N], d[N]; int main () { freopen ("column.in", "r", stdin); freopen ("column.out", "w", stdout); scanf ("%d", & n); for (int i = 1; i <= n; i ++){ scanf ("%lf", & r[i]); } for (int i = 1; i <= n ; i ++){ d[i] = r[i] * 2; for (int j = 1; j < i; j ++){ h = 2 * sqrt (r[i] * r[j]); if (d[i] + eps < d[j] - r[j] + r[i]+ h) d[i] = d[j] - r[j] + r[i] + h; } if (ans + eps < d[i]) ans = d[i]; } printf ("%.3lf", ans); fclose (stdin); fclose (stdout); return 0; }
上文思路中出現的圖片由cx大佬提供,再次感謝,順便orz