はじめに
https://atcoder.jp/contests/abc087/tasks/
の提出済みの優秀な方(kyunaさん)のコードを解説。
問題
【問題概要】
500 円玉を $A$ 枚、100 円玉を $B$ 枚、50 円玉を $C$ 枚持っています。これらの硬貨の中から何枚かを選び、合計金額をちょうど $X$ 円にする方法は何通りあるでしょうか?
【制約】
- $0 \le A, B, C \le 50$
- $A + B + C \ge 1$
- $50 \le X \le 20000$
- $A, B, C$ は整数である
- $X$ は $50$ の倍数である
【数値例】
1)
$A = 2$
$B = 2$
$C = 2$
$X = 100$
答え: $2$
条件を満たす選び方は以下の 2 通りです。
- 500 円玉を 0 枚、100 円玉を 1 枚、50 円玉を 0 枚選ぶ
- 500 円玉を 0 枚、100 円玉を 0 枚、50 円玉を 2 枚選ぶ
完成コード
お手本として、kyunaさんのコードがコチラ
a,b,c,x=map(int,open(0))
print(sum(500*i+100*j+50*k==x for i in range(a+1)for j in range(b+1)for k in range(c+1)))
一方、自分が最初に書いたコードがコチラ。ちょっと恥ずかしい。。。
愚直な感じで書き起こしました感が出てる。
a = int(input())
b = int(input())
c = int(input())
x = int(input())
count = 0
for i in range(a+1):
for j in range(b+1):
for k in range(c+1):
if i*500 + j*100 + k*50 == x:
count+=1
print(count)
メモ
見た目がだいぶ異なるけど、やってることは同じことに気付く。
自分が書いたのは単純にfor文入れ子で書いてるのに対して、お手本は「リスト内包表記」を使っているという違いがもっと大きな違い。
お手本は不要なインデントや改行や省略できるところは極力省いているけど真面目にそれらを付け加えるとこんな感じになって少しは読みやすくなる。
a,b,c,x=map(int,open(0))
print(
sum(
[500*i+100*j+50*k==x
for i in range(a+1)
for j in range(b+1)
for k in range(c+1)]
)
)
解説を加えるとi, j, kのループを回した際に500i+100j+50*kがxになる場合Trueをリストに格納し、そうでなければFalseを格納する。
で、sum()でTrueの数をカウントしてその結果をprint()する。
…といった感じ。
一応実際にリストの中身はどんなのかをみるためにsum()を外したもので実際に試してみるとこんな感じになる
a,b,c,x=map(int,open(0))
print(
[500*i+100*j+50*k==x
for i in range(a+1)
for j in range(b+1)
for k in range(c+1)]
)
-----
入力
2
2
2
100
の場合
-----
出力
[False, False, True, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False
とまぁ、こんな感じになる。
内包表記は可読性が下がるので何となく好きになれないけど、AtCoderでは重宝されることはよくわかった。
ちなみに、itertools.productを使えばforのネストを使わずにすむそうで一瞬「それ最高じゃん!」と思ったけど、引数はリスト型に限られるので
今回のケースではうまくはまらないのと、そもそもitertools.productは遅いそうで、それはそれでAtCoderでは歓迎されないようだ。なるほど。ケースバイケースってやつですね。