1. 程式人生 > >備戰Noip2018模擬賽15(A組) T2 Column 淘淘的柱子朋友

備戰Noip2018模擬賽15(A組) T2 Column 淘淘的柱子朋友

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 ......這竟然是一道純數學題,  死算

既然要距離最小,那麼每個柱子一定要與左右的柱子或者板子相切

兩個圓之間的水平r_ {1} + r_ {2} + AH距離為,如下圖所示

除此之外還有一種特殊情況,有一個半徑很小的圓柱被夾在兩個直徑較大的圓柱中間,如下圖所示

這樣我們就應該直接取更大的值

狀態設計: dp [i]表示從第一個圓柱到第i個圓柱的距離

狀態轉移:dp [i] = dp [j] + r_ {i} -r_j + \ sqrt {\ left(r_i + r_j \ right)^ {2} + \ left(r_i  -  r_j \ right)^ {2}}

初始化:

dp [i] = r [i] * 2

複雜度:\ theta \ left(n ^ {2} \ right)

程式碼

#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