(2020/03/26 追記 図形関係がほとんど間違っていたので修正しました。解答コード以外間違いくらいの勢いで間違っていた・・・)
D問題にハマってしまったのでレートダウンしました(一応ACはしました)
問題の概要
問題文
高橋君は、底面が $1$ 辺 $a \ cm$ の正方形であり、高さが $b \ cm$であるような直方体型の水筒を持っています。(水筒の厚みは無視できます。)
この水筒の中に体積 $x \ cm^3$ の水を入れ、底面の正方形の $1$ 辺を軸として、この水筒を徐々に傾けます。
水を溢れさせずに水筒を傾けることができる最大の角度を求めてください。
解き方
水筒を平面として取り扱う
奥行があると問題が複雑になるので簡略化します。底面は正方形なので水の部分の面積$S$は、
$$S = \frac{x}{a}$$
となります。
傾けた時の水の形によって場合分け
まず傾けていない時の水の形は四角形(長方形または正方形)です。傾けていくと水の形が台形になります。さらに傾けていくと水の形が三角形になります。水の形が台形の場合と三角形の場合で場合分けします。
こぼれる寸前の水の形が台形か三角形かの判定式は三角形の面積の公式より、
\left\{
\begin{array}{ll}
S > a \times b \div 2 & (台形) \\
S \leqq a \times b \div 2 & (三角形)
\end{array}
\right.
となります。
傾けた時に水の形が三角形になる時
まず考え方が簡単な三角形から解いていきます。溢れる寸前まで傾けた時に、水の高さは $b$ の所まで来ています。
求める角度は溢れる寸前の時の $∠ans$ です。$∠ans + ∠B = 90°$ 、水の入っている部分は直角三角形なので $∠A + ∠B = 90°$ 、この2つの式より $∠ans = ∠A$ となるので、 $∠A$ を求めればいいことになります。
水の入っている底面の長さ $a'$ は $S$ と $b$ が分かっているので、$ a' = 2 \times S \div b $ となります。
直角三角形での $b/a' = tan \ \theta$ です。$tan \ \theta$ の値が分かっている場合、つまり$b/a'$の値が分かっている場合の $\theta$ の求め方は逆三角関数を使います。この場合はアークタンジェントです。各プログラミング言語の数学計算ライブラリには逆三角関数を求める関数が実装されていますので、関数を利用して $\theta$ を求めます。関数の結果はラジアン単位になっていることが多いので、変換式を使って角度に変換します。ラジアンから角度への変換式は $\theta = rad \times 180 \div \pi$ です。
傾けた時に水の形が台形になる時
溢れる寸前まで傾けた時、$b$ の反対側の辺には長さ $bu$ 残っています。これでは先ほどの三角形の時の求め方ができないので、三角形になるように持っていきます。
まず台形の上底 $bu$ の長さを求めます。面積 $S$、下底 $b$、高さ $a$ が分かっているので、 $(bu + b) \times a \div 2 = S \rightarrow bu = 2 \times S \div a - b $ となります。
三角形にするには、この水筒から長さ $bu$ 分だけ切り離します。すると下図のようになります。
切り離した分だけ面積 $S'$ 、高さ $ b'$ が小さくなっていますので再計算します。
\left\{
\begin{array}{ll}
b' = b - bu \\
S' = S - a \times bu
\end{array}
\right.
あとは三角形の時と同じように計算します。
コーナーケースに注意(水が満杯に入れられた時)
水が満杯に入れられた時は台形のケースになりますが、$b'=0$となるため、$ a' = 2 \times S \div b $ のような計算をするとゼロ除算となります。$ S = a \times b $ の時はケースを分けるか、ゼロ除算しないように計算を工夫しましょう。1
解答コード
#include <bits/stdc++.h>
using namespace std;
int main(void)
{
// bits/stdc++.h をincludeしているときは定数M_PIが使えます
const double PI=3.14159265358979323846;
double a,b,x;
cin >> a >> b >> x;
double s = x / a;
double ans = 0;
double a2 = 0;
// コーナーケース
if ( s == a * b) {
ans = 0;
} else {
if (s > b * a / 2) {
// 台形の時
// 上底bu
double bu = 2 * s / a - b;
s -= a * bu;
b -= bu;
// a2 = a とするとコーナーケースを考慮する必要がなくなる
a2 = 2 * s / b;
} else {
// 三角形の時
a2 = 2 * s / b;
}
// アークタンジェント計算(結果はラジアン)
double r = atan(b/a2);
// ラジアン-角度変換
ans = r * (180.0 / PI);
}
cout << fixed << setprecision(10) << ans << endl;
return 0;
}
-
私は2WAしました ↩