あるエンジニアのAtCoder奮闘記

東京都港区にあるアミフィアブル株式会社のエンジニアが、AtCoderで解いた問題について振り返ったりしていく会社公認のブログです。

AtCoder ABC 144 D - Water Bottle (Go)

irisruneです。コンテストはE問題の解法がどうにも思いつきませんでしたが、水パフォは出ました。

問題

atcoder.jp

直球の数学問題ですが、D問題だけあって割と面倒な問題です。

package main

import (
    "bufio"
    "fmt"
    "math"
    "os"
    "strconv"
)

var sc *bufio.Scanner

func nextStr() string {
    sc.Scan()
    return sc.Text()
}

func nextInt() int {
    sc.Scan()
    i, e := strconv.Atoi(sc.Text())
    if e != nil {
        panic(e)
    }
    return i
}

func main() {
    sc = bufio.NewScanner(os.Stdin)
    sc.Buffer(make([]byte, 0), 1000000001*3)
    sc.Split(bufio.ScanWords)
    a, b, x := nextInt(), nextInt(), nextInt()
    v0 := a * a * b
    v1 := v0 - x
    switch {
    case v1 <= x:
        fmt.Println(math.Atan((float64(v1)*2.0)/float64(a*a*a)) * (180.0 / math.Pi))
    case v1 > x:
        fmt.Println(90.0 - (math.Atan((float64(x)*2.0)/float64(a*b*b)) * (180.0 / math.Pi)))
    }
}

図を描かないことには始まらない問題なので、まずは図を描いてみます。既にネタバレが見えていますが、すぐに思い浮かぶのは左側の図になると思います。

f:id:amifiable:20191028135330p:plain

この直方体の水筒において、横の辺の長さがa、縦の辺の長さがb、描写上省略されている奥行きはaです。また、水色の領域は水の入っている領域のため体積がxです。描写上水筒そのものではなく水位を傾けて表現していますが、水位と上辺の成す角度が求める角度となります。

ここで左側の図について考えると、水筒の体積v_0や求める角度\thetaについて以下の等式が成り立ちます。

v_0 = a^2b, v_0 - x = a(a\tan\theta)a/2 \Rightarrow \theta = \arctan(2(a^2b - x) / a^3)

これより角度を直接求めることができますが、図を2つ示していたことからわかるように場合分けが必要となります。場合分けの条件はxv_0/2より大きいか小さいかです。大きい場合は前述の数式で解くことができ、小さい場合は\theta'=\arctan(2(a^2b-x)/ab^2)で解くことができます(正確には90°から引いた角度が求める角度となります)。

雑記

  • 奥行きを省略した図を描いて考える場合、水の体積xを面積x/aに置き換えた方が考えやすいかもしれません。
  • 数学問題は検索でどうにかなることも多いですが、この問題については三角関数の扱い方がわかっていないと検索もままならない印象です。
    • 弧度法とラジアンの変換まで求められているのでやはり数学的知識の比重が大きめだと思います。
  • 公式解説によれば角度を直接求めるより2分法を用いる方が素直な解き方らしいです。いずれにせよ三角関数を用いる必要はあります。