// 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;
}
UVa Problem 10012 How Big Is It? (它有多大?)
最新推荐文章于 2020-04-20 19:07:41 发布