UVa 267 Of(f) Course
题目分析
题目背景是飞机在风场中的导航问题。航空公司发现其导航系统没有正确考虑风速和风向,导致飞机偏离航线。需要编写程序,根据给定的期望航线、真空速、风速和风向,计算出飞机应该指向的航向(Heading\texttt{Heading}Heading)以及地速(Ground Speed\texttt{Ground Speed}Ground Speed)。
输入包含多组数据,每组数据包含四个浮点数:风速、风向、期望航线、真空速。所有速度单位为节(knots\texttt{knots}knots),方向单位为度(0∘0^{\circ}0∘ 表示北,90∘90^{\circ}90∘ 表示东,180∘180^{\circ}180∘ 表示南,270∘270^{\circ}270∘ 表示西)。
输出需要先原样打印输入的四项数据,然后打印计算得到的飞机航向和地速,每个数字精确到小数点后两位。需要注意:方向不能输出 −0.00-0.00−0.00 或 360.00360.00360.00,速度不能输出 −0.00-0.00−0.00。
解题思路
矢量三角形模型
这是一个典型的矢量合成问题。在风场中,飞机的运动由三个矢量决定:
- 真空速矢量 Va⃗\vec{V_a}Va:飞机相对于空气的速度,大小为真空速(TRUE AIRSPEED\texttt{TRUE AIRSPEED}TRUE AIRSPEED),方向为飞机航向(HEADING\texttt{HEADING}HEADING,即机头指向)。
- 风速矢量 Vw⃗\vec{V_w}Vw:空气相对于地面的速度,大小为风速(WIND SPEED\texttt{WIND SPEED}WIND SPEED),方向为风的来向(WIND DIRECTION\texttt{WIND DIRECTION}WIND DIRECTION,注意是风从何处吹来)。
- 地速矢量 Vg⃗\vec{V_g}Vg:飞机相对于地面的速度,大小为地速(GROUND SPEED\texttt{GROUND SPEED}GROUND SPEED),方向为期望航线(DESIRED COURSE\texttt{DESIRED COURSE}DESIRED COURSE)。
这三个矢量满足关系:
Vg⃗=Va⃗+Vw⃗ \vec{V_g} = \vec{V_a} + \vec{V_w} Vg=Va+Vw
通常,已知的是:
- 期望航线(地速的方向)
- 真空速(Va⃗\vec{V_a}Va 的大小)
- 风速(Vw⃗\vec{V_w}Vw 的大小)
- 风向(Vw⃗\vec{V_w}Vw 的方向)
需要求解的是:
- 航向(Va⃗\vec{V_a}Va 的方向)
- 地速(Vg⃗\vec{V_g}Vg 的大小)
几何关系
将矢量关系画成三角形:以 Vw⃗\vec{V_w}Vw 的终点为起点作 Va⃗\vec{V_a}Va,则 Vg⃗\vec{V_g}Vg 从 Vw⃗\vec{V_w}Vw 的起点指向 Va⃗\vec{V_a}Va 的终点。在这个三角形中:
- Vw⃗\vec{V_w}Vw 和 Va⃗\vec{V_a}Va 之间的夹角 α\alphaα 等于风向与航向的夹角。
- Vg⃗\vec{V_g}Vg 与 Vw⃗\vec{V_w}Vw 之间的夹角等于期望航线与风向的夹角(注意风向的定义)。
设:
- www = 风速
- dwd_wdw = 风向(风从该方向吹来)
- ccc = 期望航线
- aaa = 真空速
- hhh = 航向(待求)
- ggg = 地速(待求)
首先计算期望航线与风向之间的夹角差:
θ=∣dw−c∣ \theta = |d_w - c| θ=∣dw−c∣
若 θ>180∘\theta > 180^{\circ}θ>180∘,则取 θ=360∘−θ\theta = 360^{\circ} - \thetaθ=360∘−θ。
特殊情况处理
-
风向与期望航线相同(θ≈0∘\theta \approx 0^{\circ}θ≈0∘):
- 顺风飞行,航向等于期望航线,地速 = 真空速 + 风速。
代码中的处理需要仔细分析:题目中 dwd_wdw 是风来的方向,ccc 是期望航线。当 θ=0\theta = 0θ=0 时,风从正前方或正后方吹来?实际上,若 dw=cd_w = cdw=c,表示风从期望航线的正前方吹来,这是逆风情况,地速 = 真空速 - 风速。但原题解的代码中当
angle <= EPSILON时输出air_speed - wind_speed,这是逆风情况。当angle接近 180∘180^{\circ}180∘ 时输出air_speed + wind_speed,这是顺风情况。 -
风向与期望航线相反(θ≈180∘\theta \approx 180^{\circ}θ≈180∘):
- 逆风飞行,航向等于期望航线,地速 = 真空速 - 风速(实际应为顺风,请结合代码注释理解)。
一般情况下的计算
当 0∘<θ<180∘0^{\circ} < \theta < 180^{\circ}0∘<θ<180∘ 且 θ≠0,180\theta \neq 0,180θ=0,180 时,使用正弦定理和余弦定理求解。
设 β\betaβ 为期望航线与航向之间的夹角(即风修正角),γ\gammaγ 为风向与期望航线之间的夹角。
由矢量三角形,根据正弦定理:
sinβw=sinθa \frac{\sin \beta}{w} = \frac{\sin \theta}{a} wsinβ=asinθ
其中 θ\thetaθ 是风向与期望航线之间的夹角(已规范化到 [0,180][0,180][0,180])。则有:
β=arcsin(wsinθa) \beta = \arcsin\left( \frac{w \sin \theta}{a} \right) β=arcsin(awsinθ)
然后可以确定航向:
- 若风向在期望航线的左侧,则航向 = 期望航线 - β\betaβ
- 若风向在期望航线的右侧,则航向 = 期望航线 + β\betaβ
确定 β\betaβ 的符号需要判断风向相对于期望航线的方向。
求得航向后,再根据地速矢量与风速矢量的夹角关系,使用余弦定理求地速:
g=a2+w2−2awcosβ g = \sqrt{a^2 + w^2 - 2aw \cos \beta} g=a2+w2−2awcosβ
或者等价地:
g=a2−w2sin2θ+wcosθ g = \sqrt{a^2 - w^2 \sin^2 \theta} + w \cos \theta g=a2−w2sin2θ+wcosθ
原代码中使用的公式与此等价。
角度规范化
计算得到的航向可能超出 [0,360)[0, 360)[0,360) 范围,需要进行规范化:
- 若航向 ≥360\ge 360≥360,则减去 360360360
- 若航向 <0< 0<0,则加上 360360360
同时要避免输出 360.00360.00360.00,应输出 0.000.000.00(原代码中注释部分处理了此问题)。
代码实现
// Of(f) Course!
// UVa ID: 267
// Verdict: Accepted
// Submission Date: 2016-05-27
// UVa Run Time: 0.050s
//
// 版权所有(C)2016,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const double PI = 2.0 * acos(0.0), EPSILON = 1E-7;
int main(int argc, char *argv[])
{
cin.tie(0);
cout.sync_with_stdio(false);
double wind_speed, wind_direction, air_speed, desired_course;
// 循环读取每组输入数据,直到文件结束
while (cin >> wind_speed >> wind_direction >> desired_course >> air_speed)
{
// 设置输出格式:保留两位小数,固定小数点格式
cout.precision(2);
cout.setf(ios::fixed);
// 输出输入的四项数据
cout << "WIND SPEED " << wind_speed << "\n";
cout << "WIND DIRECTION " << wind_direction << "\n";
cout << "DESIRED COURSE " << desired_course << "\n";
cout << "TRUE AIRSPEED " << air_speed << "\n";
// 计算期望航线与风向之间的夹角(绝对值)
double angle = fabs(wind_direction - desired_course);
// 特殊情况1:风向与期望航线相同(夹角为0度)
if (angle <= EPSILON)
{
cout << "AIRCRAFT HEADING " << desired_course << "\n";
cout << "GROUND SPEED " << (air_speed - wind_speed) << "\n";
cout << "\n";
continue;
}
// 特殊情况2:风向与期望航线相反(夹角为180度)
else if (fabs(angle - 180.0) <= EPSILON)
{
cout << "AIRCRAFT HEADING " << desired_course << "\n";
cout << "GROUND SPEED " << (air_speed + wind_speed) << "\n";
cout << "\n";
continue;
}
// 将夹角规范化到 [0, 180] 范围
else if (angle > 180.0)
{
angle = 360.0 - angle;
}
// 计算真空速矢量与风速矢量之间的夹角(转换为弧度)
double angle_of_air_speed = 180.0 - angle;
angle_of_air_speed = angle_of_air_speed / 180.0 * PI;
// 计算地速:使用矢量分解方法
// 公式:g = sqrt(a^2 - w^2 * sin^2(theta)) + w * cos(theta)
double ground_speed = sqrt(pow(air_speed, 2) - pow(wind_speed, 2) +
pow(wind_speed * cos(angle_of_air_speed), 2)) + wind_speed * cos(angle_of_air_speed);
// 计算风修正角(航向与期望航线之间的偏差)
// 使用余弦定理:cos(bias) = (a^2 + g^2 - w^2) / (2 * a * g)
double bias = acos((pow(air_speed, 2) + pow(ground_speed, 2) -
pow(wind_speed, 2)) / (2.0 * air_speed * ground_speed));
// 将偏差从弧度转换为度数
bias = bias / PI * 180.0;
// 根据风向相对于期望航线的位置,确定航向的修正方向
if (fabs(wind_direction - desired_course) > 180.0)
{
if (desired_course > wind_direction)
desired_course += bias;
else
desired_course -= bias;
}
else
{
if (desired_course > wind_direction)
desired_course -= bias;
else
desired_course += bias;
}
// 将航向规范化到 [0, 360) 范围
if (desired_course >= 360.0)
desired_course -= 360.0;
if (desired_course < 0.0)
desired_course += 360.0;
// 注意:原代码中有一段注释掉的字符串流处理,用于将 360.00 转换为 0.00
// 由于浮点数精度问题,实际测试中直接输出 desired_course 也能通过
cout << "AIRCRAFT HEADING " << desired_course << "\n";
cout << "GROUND SPEED " << ground_speed << "\n";
cout << "\n";
}
return 0;
}
总结
本题的核心是理解矢量合成中的三角形关系,通过正弦定理和余弦定理求解未知的航向和地速。需要注意风向的定义(风从某方向吹来)以及角度的规范化处理。代码中处理了顺风和逆风的特殊情况,并通过精确的浮点计算保证精度。输出格式要求严格,需要保留两位小数,避免出现 -0.00 或 360.00 等不符合规范的数值。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)