UVa Problem 10012 How Big Is It? (它有多大?)

本文介绍了一种算法,用于通过回溯遍历所有可能的圆环排列,计算其宽度,并找出最小值。该算法在计算圆环宽度时考虑了一些细节,包括圆环可能完全在另一个圆环下面而不超出其半径范围的情况。
// 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;
}


在解决UVA133-The Dole Queue问题时,运用队列这一数据结构是关键。首先,应熟悉队列的基本操作,包括入队(enqueue)、出队(dequeue)以及查看队首(peek)元素。这些操作对于实现题目中描述的排队、服务和添加新申请者的逻辑至关重要。 参考资源链接:[掌握算法技巧:UVA133救济金发放问题详解](https://wenku.youkuaiyun.com/doc/20610tuj1w?spm=1055.2569.3001.10343) 具体到算法实现,可以将每个申请者看作是队列中的一个元素。新申请者入队到队尾,而服务则是在队首元素上进行,服务完成后该元素出队。在这个过程中,需要注意的是,题目可能会有特殊规则,例如某个人的优先级高于其他人,那么我们需要对队列进行适当调整以满足这些规则。 为了更好地掌握这些概念,并将理论应用到实际编程中,我强烈推荐查阅《掌握算法技巧:UVA133救济金发放问题详解》这份资料。它不仅包含了对UVA133题目的深入解析,还提供了具体的编程实践指导和问题解决方案,能够帮助你更有效地理解和掌握使用队列解决这类问题的方法。 实现算法时,可以使用数组或链表来模拟队列的操作。首先,定义队列的基本结构和操作函数,然后编写函数处理特定的情况,如根据优先级对队列进行调整。在编写代码的过程中,务必注意代码的可读性和健壮性,这样才能在UVA平台上顺利通过所有的测试用例。 通过这样的实战练习,你不仅可以加深对队列数据结构的理解,还可以提升解决实际问题的能力。一旦掌握了UVA133-The Dole Queue问题,你将能够应对更涉及队列操作的算法题目。为了进一步巩固和拓展你的知识,建议在解决了这个问题之后,继续在UVA平台或其他在线编程平台上练习更的算法题目,同时参考更相关的教程和资料。 参考资源链接:[掌握算法技巧:UVA133救济金发放问题详解](https://wenku.youkuaiyun.com/doc/20610tuj1w?spm=1055.2569.3001.10343)
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值