地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1608
题意:给你两个圆形(半径分别为 r1 、r2 )和一个矩形(矩形边长为 a 和 b),判断两个圆是否能放于矩形内。其中 a, b, r1, r2 都是浮点数。
分析:考虑两个圆能放入的一个最基本的必要条件就是:半径最大的圆至少要能放入,那么小圆自然也能放入,即 min( a, b ) >= max( r1, r2 ) * 2;
接下来分析能否放入的临界点。假设矩形的长边是 a,短边是 b,大圆半径是 r1, 小圆半径是 r2,满足上述条件时( b >= r1 * 2 ),r2 很小的情况可以不必考虑。
另 b 略大于 r1 * 2,设想右侧短边是一个可以滑动的挡板,从远处向左侧短边滑动。 当 a 非常大时,两个圆靠近成相切状态,两个圆可以同时与底边相切(但这不是最节省空间状态)。当矩形向内收缩时,达到下图所示状态时(两个圆分别和矩形相对的边相切,即理想状态下矩形每条边只和一个圆相切,只有一个切点),将不能继续收缩,即到达临界点:
我们也可以从另一个角度理解,假设上图是一个内壁十分光滑的容器(可以是集装箱或者汽油桶)的截面,底部的宽度固定为 a,且略大于大圆(表面光滑的圆柱体或者球体)直径(2*r1),先把大圆放入容器,再把小圆放入,则必然因为重力作用,小圆把大圆挤向容器的另一侧,稳定于上述图形的状态。
设图中红色直角三角形的两个直角边边分别为 x,y,斜边为 z,则:
z = r1 + r2;
x = a - z;
y = b - z;
其中斜边 z 是两圆相切时的圆心距离,可看作是固定的。直角边 x、y 主要取决于矩形边长。因此当 z^2 <= x^2 + y^2 时意味着矩形足够大,可以放入矩形内。否则不能放入。下面的代码中函数 judge 的返回值含义看做是 BOOL 类型,表示是否可放入。
#include <math.h> #include <stdio.h> int judge(double a, double b, double r1, double r2) { double maxside = a > b ? a : b; double minside = a < b ? a : b; double maxR = r1 > r2 ? r1 : r2; double minR = r1 < r2 ? r1 : r2; if(minside < maxR * 2) return 0; double z = r1 + r2; double x = a - z; double y = b - z; if(x * x + y * y < z * z) return 0; else return 1; } int main(int argc, char* argv[]) { double a, b, r1, r2; while(scanf("%lf %lf %lf %lf", &a, &b, &r1, &r2) != EOF) { if(judge(a, b, r1, r2)) printf("Yes\n"); else printf("No\n"); } return 0; }
最后,题目中的“You can safely assume x < y, where x and y are float-point numbers, if x < y + 0.01.” 这句话是什么意思呢,其实就是让你不必关注浮点数的大小比较,其两个浮点数的大小关系将是非常显著的。我推测出题者的意思就是测试数据的浮点数关系不是大于就是小于(明显不能放入或明显能放入),而不会出现非常接近(非常靠近能否放入的临界点),例如 1.00001 和 1.00002 这样的让人比较纠结的情况。所以上面的代码中不必考虑浮点计算和比较时的精度问题,即分析和代码中的所有等于号“=”应该都是可以去掉的。如果两个浮点数非常接近,例如给出一个矩形的边分别是 5.0000 和 4.9999,满足 5.0000 < (4.9999 + 0.1 ),则导致可以认为 5.0000 < 4.9999,这显然是荒唐的。