题目大意:对于蛋糕(圆柱体),切两刀(分别给出两点,确定一刀所在直线),分成四块蛋糕,求出最大蛋糕和最小蛋糕的体积。
切刀的交点不一定是蛋糕圆心,确定每一刀方向的两点也不一定在圆上;
解题策略:见代码注释。
小 结:
题整体难度不大,综合性较强(直线相交,圆与直线相交,扇形面积,三角形面积等);
这道题做完要开始复习了,深深感觉到
“Top-Down”编程风格或者说是编程思想的重要性,整体把握,模块实现,
调试,可读性都会成倍提升,努力培养自己良好的编程风格,或许这就是Mr.Zhu所说的看不见的东西吧!
/*
UVA 11373 Happy Birthday
AC by J_Dark
Time 0.015s
ON 2013/5/17
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <climits>
#include <vector>
#include <algorithm>
using namespace std;
const double eps = 1e-16;
const double PI = 3.141592653;
struct point{
double x, y;
point(double a=0, double b=0){ x = a; y = b; }
double Distance(point t){
return sqrt( (t.x-x)*(t.x-x) + (t.y-y)*(t.y-y) );
}
int Pos;
};
struct line{
point u, v;
line(point a, point b){ u = a; v = b; }
};
vector<point> LC; //记录直线与圆四个交点及两直线交点P
vector<line> L; //记录切蛋糕两条直线以及由圆心与交点P确定的直线
double x10, y10, x11, y11, x20, y20, x21, y21, rad, height, MaxCake, MinCake;
point center(0,0);
void Input(){
LC.clear();
L.clear();
L.push_back(line(point(x10, y10), point(x11, y11)));
L.push_back(line(point(x20, y20), point(x21, y21)));
}
//计算直线与圆交点
void LC_InsectNode(line p){
point t1, t2;
if(fabs(p.u.x - p.v.x) < eps){
t1.x = t2.x = p.u.x;
t1.y = sqrt(rad*rad - t1.x*t1.x);
t2.y = -t1.y;
LC.push_back(t1);
LC.push_back(t2);
}
else{
//将直线两点式转换为y=k*x+b形式,与圆方程x*x+y*y=r*r联立,求解交点
double k, b;
k = (p.u.y - p.v.y)/(p.u.x - p.v.x);
b = p.u.y - k*p.u.x;
double mm = 4*k*k*b*b - 4*(k*k+1)*(b*b-rad*rad);
t1.x = (-2*k*b + sqrt(mm)) / (2*(k*k+1)); //又是特么的计算式分母忘记加括号,结果特么wa那么久,擦擦擦……
t2.x = (-2*k*b - sqrt(mm)) / (2*(k*k+1));
t1.y = k*t1.x + b;
t2.y = k*t2.x + b;
LC.push_back(t1);
LC.push_back(t2);
}
}
//叉积计算三角形面积
double AreaTrangle(line a, line b){
return fabs(( (a.v.x-a.u.x)*(b.v.y-b.u.y)-(b.v.x-b.u.x)*(a.v.y-a.u.y) )/2);
}
double Direction(point Pi, point Pj, point Pk){
return (Pj.x-Pi.x)*(Pk.y-Pi.y)-(Pk.x-Pi.x)*(Pj.y-Pi.y);
}
//求两直线交点
void LL_InsectNode(line p, line q){
double d1, d2, d3, d4;
point t;
d1 = Direction(p.u, p.v, q.u);
d2 = Direction(p.u, p.v, q.v);
d3 = Direction(q.u, q.v, p.u);
d4 = Direction(q.u, q.v, p.v);
t.x = (q.u.x*d2 - q.v.x*d1)/(d2 - d1);
t.y = (q.u.y*d2 - q.v.y*d1)/(d2 - d1);
LC.push_back(t);
}
//若q两端点在p同侧,返回true
bool isCross(line p, line q){
double d1, d2, d3, d4;
d1 = Direction(p.u, p.v, q.u);
d2 = Direction(p.u, p.v, q.v);
if(d1*d2>0) return true;
else return false;
}
/*
“排序”:
1,按照象限顺时针排序
2,象限相同,叉积判断排序
*/
bool cmp(point a, point b){
if(a.Pos == b.Pos){
if(a.Pos == 1 || a.Pos == 4) return a.y < b.y;
if(a.Pos == 2 || a.Pos == 3) return a.y > b.y;
}
return a.Pos < b.Pos;
}
void Sort(){
for(int i=0; i<LC.size(); i++){
if(LC[i].x<=0 && LC[i].y>=0) LC[i].Pos = 1;
if(LC[i].x>0 && LC[i].y>=0) LC[i].Pos = 2;
if(LC[i].x<=0 && LC[i].y<0) LC[i].Pos = 4;
if(LC[i].x>0 && LC[i].y<0) LC[i].Pos = 3;
}
sort(LC.begin(), LC.end(), cmp);
}
void Compute(){
//计算两条直线与圆交点
LC_InsectNode(L[0]);
LC_InsectNode(L[1]);
//按照圆弧顺时针方向对交点排序
Sort();
//计算两条直线交点p
LL_InsectNode(L[0], L[1]);
MaxCake = INT_MIN;
MinCake = INT_MAX;
double As, Ao, Ap, aa, bb, cc;
L.push_back(line(LC[4], center));
for(int i=0; i<4; i++){
//余弦定理计算扇形圆心角
aa = LC[i].Distance(center);
bb = LC[(i+1)%4].Distance(center);
cc = LC[i].Distance(LC[(i+1)%4]);
double Angle = acos( (aa*aa+bb*bb-cc*cc)/(2*aa*bb) );
Ao = AreaTrangle(line(center, LC[i]), line(center, LC[(i+1)%4]));
Ap = AreaTrangle(line(LC[4], LC[i]), line(LC[4], LC[(i+1)%4]));
line t(LC[i], LC[(i+1)%4]);
double Ac;
if(isCross(t, L[2])){
//若在同侧,计算式如下
As = Angle*rad*rad / 2;
Ac = As - Ao + Ap;
}
else{
/*
若在异侧,还需判断角度是否钝角,
是——其所对弧应为优弧,圆心角为360减之
计算式如下
*/
if((aa*aa+bb*bb-cc*cc)/(2*aa*bb) < eps) Angle = 2*PI - Angle;
As = Angle*rad*rad / 2;
Ac = As + Ao + Ap;
}
MaxCake = max(MaxCake, Ac);
MinCake = min(MinCake, Ac);
}
}
void Output(){
printf("%.2lf %.2lf\n", MaxCake*height, MinCake*height);
}
/
int main(){
while(cin >> rad >> height >> x10 >> y10 >> x11 >> y11 >> x20 >> y20 >> x21 >> y21)
{
Input();
Compute();
Output();
}
//system("pause");
return 0;
}