geek.conf.2

あるエンジニアの備忘録

AtCoder Beginner Contest 095 C - Half and Half の解法考察 Pythonで書いてます

最近、AtCoderをボチボチやっていて、すごく書きたくなって書きました。 

問題はこちら
C - Half and Half

A・・・Aピザの金額
B・・・Bピザの金額
C・・・ABピザの金額
X・・・Aピザの金額
Y・・・Aピザの金額

求めるものは最小のAピザ、Bピザの必要枚数分の金額
注意点は各ピザを多く買ってもいいってこと。

まずA+B<=2Cかどうかチェックする。A+B<=2CならABピザを買うと割高なので、ABピザは買わずにAピザ、Bピザをそれぞれ買えばよろしい。

んで、A+B>=2Cの場合、とりあえずABピザでAピザ、Bピザのうち少ない枚数の方を賄っちゃって、残りをまたABピザを買って賄うか、ABピザを買わずに、Aピザ、Bピザをそれぞれ買うのかを比較する。
なぜ少ない枚数の方をABピザで賄うかというとABピザで全てを賄わないパターンを比較対象に含めるためである。
なのでAピザ、Bピザの枚数が少ない方をチェックする。X<=Yのときは2CX+2C(Y-X)=2CY or 2CX+B(Y-X)だし、X>=Yのときは2CY+2C(X-Y)=2CX or 2CY+A(X-Y)との比較となる。
ここでY-Xについてだが、
例えば、X<=Yのとき、つまりAピザ、Bピザのうち少ない枚数がAピザのX枚の方だとしたらCを2枚、X枚買うとそれはつまり、AピザX枚とBピザX枚である。残りはBピザY枚からBピザX枚を引いたBピザの枚数Y-X枚となる。

これをコードで表すとこちらになる。

A, B, C, X, Y = map(int, input().split())

if A+B <= 2*C:
    print(A*X+B*Y)
else:
    if X <= Y:
        print(min(2*C*Y, 2*C*X+B*(Y-X)))
    else:
        print(min(2*C*X, 2*C*Y+A*(X-Y)))

しかし、しかしだ。僕はこの問題を全探索の練習問題として紹介されて解き始めた。なので上記はやりたい解法ではなかった。
金額A,B,Cのパターン全探索すると時間がかかる。ABCXYを数式化してみよう。ABピザの枚数をZ枚として、、、

A(X-Z)+B(Y-Z)+2CZ

2CZ=AZ+BZなのでAXとBYから引いてあげれば帳尻が合う。

となるとコードはこちらになる。

A, B, C, X, Y = map(int, input().split())
ans = 2*(5000*10**5)
for Z in range(10**5+1):
    total = A*max(X-Z, 0)+B*max(Y-Z, 0)+2*C*Z
    ans = min(total,ans)
print(ans)

全探索のパターンを減らすには、問題の登場人物の関係性を特定して数式化する必要があるけど、そこが難しいんだよね。