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