// How Big Is It? (它有多大?) // PC/UVa IDs: 111308/10012, Popularity: B, Success rate: low Level: 3 // Verdict: Accepted // Submission Date: 2011-11-05 // UVa Run Time: 0.272s // // 版权所有(C)2011,邱秋。metaphysis # yeah dot net // // [解题方法] // 通过回溯遍历所有可能的圆环排列,计算其宽度,然后取最小值即为所求,在计算圆环宽度时需要注意一些 // 细节。 #include <iostream> #include <cstring> #include <iomanip> #include <cmath> using namespace std; #define MAXN 8 struct cycle { double x, y; double r; }; double smallestWidth; // 计算指定顺序的圆环排列的宽度。 double calWidth(double radius[], int m, int used[]) { double width = 0.0; cycle packedCycles[MAXN]; // 以盒子的左下角为直角坐标系原点(0,0),计算最后放置的圆环圆心坐标,其横坐标加上其 // 半径即是盒子的宽度。在计算时,需要考虑到圆环可能完全在某一个圆环下面而没有超出较大圆 // 环的半径之外。 packedCycles[0] = (cycle){radius[used[0]], radius[used[0]], radius[used[0]]}; for (int i = 1; i < m; i++) { // 当前为第 i 个圆环安排位置。假设圆环与编号为 0 - (i - 1)的圆环相切,检测 // 是否存在矛盾,若无矛盾,则第 i 个圆环与此圆环相切,进而可以计算圆心坐标。 for (int j = i - 1; j >= 0; j--) { // 圆环的圆心坐标。 double tmpX = packedCycles[j].x, tmpY = radius[used[i]]; // 圆心距及纵坐标之差。 double centerDist = packedCycles[j].r + radius[used[i]]; double yDiff = fabs(packedCycles[j].r - radius[used[i]]); // 由勾股定理计算横坐标之差。注意要处理特殊情况,若第一个圆环比较小, // 则后面的圆环可能超出左边界,故其横坐标至少为其半径。 tmpX += sqrt(pow(centerDist, 2) - pow(yDiff, 2)); if (tmpX < radius[used[i]]) tmpX = radius[used[i]]; // 检测是否存在冲突,即两者圆心的距离是否大于两个圆环半径之和。 bool successed = true; for (int k = 0; k < i; k++) if (k != j) { centerDist = pow(tmpX - packedCycles[k].x, 2); centerDist += pow(tmpY - packedCycles[k].y, 2); centerDist = sqrt(centerDist); if (centerDist < (packedCycles[k].r + radius[used[i]])) { successed = false; break; } } // 圆环放置无冲突。 if (successed) { packedCycles[i] = (cycle){tmpX, tmpY, radius[used[i]]}; break; } } } // 返回盒子的宽度,取圆心坐标加上半径的最大值,因为最后一个圆不一定是最靠右的。 double maxRight = 0.0; for (int i = 0; i < m; i++) maxRight = max(maxRight, packedCycles[i].x + packedCycles[i].r); return maxRight; } // 回溯遍历所有可能的圆环排列。 void backtrack(double radius[], int m, int c, int used[], bool unused[]) { if (c == m) smallestWidth = min(smallestWidth, calWidth(radius, m, used)); else { for (int i = 0; i < m; i++) if (unused[i]) { unused[i] = false; used[c] = i; backtrack(radius, m, c + 1, used, unused); unused[i] = true; } } } int main(int ac, char *av[]) { int cases, m; double radius[MAXN]; int used[MAXN]; bool unused[MAXN]; cout.precision(3); cout.setf(ios::fixed | ios::showpoint); cin >> cases; while (cases--) { cin >> m; smallestWidth = 0.0; for (int i = 0; i < m; i++) { cin >> radius[i]; smallestWidth += radius[i]; } smallestWidth *= 2.0; memset(unused, true, sizeof(unused)); backtrack(radius, m, 0, used, unused); cout << smallestWidth << endl; } return 0; }