题目大意:给出一些园,求一个可以将所有圆装下的箱子,输出箱子的长,圆摆放的要求是必须至少与另一个圆相接,并且所有圆必须接触地面。
解题思路:一开始吧这道题想的太简单了,直接用DFS将所有排列枚举出来,加上两圆距离的公式(2 * sqrt(a * b))求完事了,其实远远不够,本题还有一些情况特殊的情况,例如两个大圆相切后,中间可以放一个小圆,这样的话小圆的长度就将被忽略而不能计算进总长度。
考虑进上面提到的特殊情况以后,我的做法是将所有圆排序,然后用下一个序列的方法对所有能组成的序列进行检查,检查的时候要注意考虑大小大夹小圆等特殊情况。
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
const int N = 105;
const double INF = 0x7fffffff;
int n;
double r[N], x[N], y[N], ans;
void init() {
ans = INF;
memset(r, 0, sizeof(r));
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%lf", &r[i]);
sort(r, r + n);
}
double dis(int a, int b) {
return sqrt(r[a] * r[b]) * 2;
}
double lenth(int a, int b) {
return sqrt( pow(x[a] - x[b], 2) + pow(y[a] - y[b], 2));
}
bool cmp(double a, double b) {
return a - b < -1e-9;
}
void solve() {
memset(x, 0, sizeof(x));
memset(y, 0, sizeof(y));
x[0] = y[0] = r[0];
double Max = 2 * r[0];
for (int i = 1; i < n; i++) {
x[i] = x[i - 1] + dis(i - 1, i);
y[i] = r[i];
if (cmp(x[i], y[i])) x[i] = y[i];
for (int j = 0; j < i; j++) {
if (cmp( lenth(i, j), r[i] + r[j]) )
x[i] = x[j] + dis(i, j);
}
if (cmp(Max, x[i] + r[i]) ) Max = x[i] + r[i];
}
if (cmp(Max, ans) ) ans = Max;
}
int main () {
int cas;
scanf("%d", &cas);
while (cas--) {
init();
do {
solve();
} while (next_permutation(r, r + n));
printf("%.3lf\n", ans);
}
return 0;
}