LoginSignup
1
1

ABC306をPythonで解いてみたよ。(A~E問題)

Last updated at Posted at 2023-06-17

AtCoder Beginners Contest 306 (ABC306) をPythonで解きました。
見やすいコードを書く練習も兼ねてます。

A - Echo

問題ページ

考察

文字を1つずつ見ていって、その文字を2回出力します。
Pythonのprint関数はデフォルトだと改行が入ってしまうので、おまじないで第2引数にend=''とつけてあげます。

print関数のendについて 先に結論から書くと、endのところには出力した後の末尾を書きます。 たとえばこんな感じ。
# 末尾にxをつけて出力するならこう。
for i in range(4):
    print(i, end='x')  # 0x1x2x3x

# よくあるのは下の2つ。
# 改行なし、全部ぴったりくっつけて出力
for i in range(4):
    print(i, end='')  # 0123

# 改行なし、半角スペースをあけて出力
for j in range(4):
    print(i, end=' ')  # 0 1 2 3

コード

N = int(input())
S = input()
for s in S:
    print(s + s, end='')

B - Base2

問題ページ

考察1

2進数で考えます。
たとえばサンプル1は答えが$13$になっていて、2進数表記すると、$13=(1101)_2$ です。
$A_0$が1の位、$A_1$が10の位、...となっていて、普段見ている数字と左右が逆になっているのに注意します。
リストAの左右を反転させて、それを2進数だと思って10進数表記に直してあげればACです。

コード1

A = list(input().split())
A = A[::-1]   # 左右の反転でよくつかいます!
N = ''.join(A)  # リストを1つの文字列にする。
print(int(N, 2))  # 2進数の文字列を10進数の数に変換する。

考察2

こっちの方が簡単で、問題文の通りに式を立てます。
(Pythonだとオーバーフローが起こらなくて、これはPython有利(?)な問題だと思います!)

コード2

A = list(map(int,input().split()))

ans = 0
for i in range(64):
    ans += A[i] * (2 ** i) 
print(ans)

C - Centers

問題ページ

考察

それぞれの数字について、何番目に現れたのかを記録します。
具体的には、$A[i]=num$のときリスト$G[num]$に$i$を格納します。
同じ数字は3回出てくるらしいので、どの$i$についても$G[i]$の中には3つしか数字が入っていません。
問題文で出てくる$f(i)$は、$G[i][1]$ のことになります。
$f(i)$の小さいものから順に$i$を出力すればいいので、リストPを用意して、その中に$(f(i), i)$のタプルを格納していって、最後にPを昇順にソートすればいいです。

コード

N = int(input())
A = list(map(int, input().split()))

G = [[] for _ in range(N)]

for i, a in enumerate(A):
    G[a - 1].append(i)

P = []
for i in range(N):
    P.append((G[i][1], i + 1))
P.sort()
for a, b in P:
    print(b, end=' ')

D - Poisonous Full-Course

問題ページ

考察

動的計画法DPで解きます。次のように2次元リストdpを決めます。
$dp[i][flag]$: $i$個目までの品物が出てきて、高橋くんの状態がflag(0なら元気、1なら腹痛)のときの、食べた料理のおいしさの総和
初期状態は $dp[0][0]=0$ です。

先に出された料理を食べないときを考えます。次のように遷移します。
$dp[i+1][0]=dp[i][0]$
$dp[i+1][1]=dp[i][1]$

次に出された料理を食べる時を考えます(おいしさを$v$とします)。
料理に毒が盛られていないとき、
$dp[i+1][0]=max(dp[i+1][0], dp[i][0]+v, dp[i][1]+v)$
料理に毒が盛られているとき、腹痛だと食べられないことに注意します。
$dp[i+1][1]=max(dp[i+1][1], dp[i][0]+v)$

答えは$max(dp[N-1][0], dp[N-1][1])$です。

コード

INF = 1 << 80

N = int(input())
P = [list(map(int, input().split())) for _ in range(N)]

# dp[i][flag]: max_score
# flag=0: 元気、 flag=1: やばい
dp = [[-INF] * 2 for _ in range(N + 1)]
dp[0][0] = 0

for i in range(N):
    dp[i + 1] = dp[i][:]
    x, v = P[i]
    if x == 0:
        dp[i + 1][0] = max(max(dp[i]) + v, dp[i + 1][0])
    else:
        dp[i + 1][1] = max(dp[i][0] + v, dp[i + 1][1])

print(max(dp[-1]))

E - Best Performances

問題ページ

考察

tatyamさん作のSortedMultiSetをつかいます(いつもありがとうございます!)。
大きい方から上位k個を入れておくSortedMultiSetと、それ以外のSortedMultiSetをつくります。
また、1つ目のSortedMultiSetに入ってる数の総和を表す変数totも用意しておきます。

あとは実際にシミュレートするのみです。
シミュレートするのに上の3つが必要だということが分かりますか?というところが1つ目の壁で、これを時間内にコードにできますか?というのが2つ目の壁な気がします。

どうシミュレートするかを書くとかなり長くなるので、詳しくはコードを見てください。

コード

'''
tatyamさん作の、SortedMultisetです。
使わせていただき、ありがとうございます!
https://github.com/tatyam-prime/SortedSet/blob/main/SortedMultiset.py

SortedSetの多重集合版。同じ要素を複数入れることができる。
SortedSetから、s.add(x), s.discard(x) が変更され、s.count(x)が追加されている。

・使い方(個人的まとめ)
s=SortedMultiSet()
s.a: SortedMultiSetの中身を返す。
len(s), x in s, x not in s: リストと同じ要領で使える。
s.add(x): xを追加する。sにxが含まれているかどうかは関係ない。
s.discard(x): xを1つだけ削除してTrueを返す。もし存在しないなら、Falseを返す。
s.count(x): sに含まれるxの個数を返す。
s.lt(x): xより小さい最大の要素を返す。もし存在しないなら、Noneを返す。
s.le(x): x 以下の 最大の要素を返す。もし存在しないなら、Noneを返す。
s.gt(x): xより大きい最小の要素を返す。もし存在しないなら、Noneを返す。
s.ge(x): x 以上の 最小の要素を返す。もし存在しないなら、Noneを返す。
s.index(x): xより小さい要素の数を返す。
s.index_right(x): x以下の要素の数を返す。

・使い方URL
https://github.com/tatyam-prime/SortedSet
'''
# (SortedMultiSetが長いから省略。上のURLからコピペしてね。)

N, K, Q = map(int, input().split())

A = [0] * N
use_set = SortedMultiset()
for _ in range(K):
    use_set.add(0)
gar_set = SortedMultiset()  # garbage
for _ in range(N - K):
    gar_set.add(0)
tot = 0

for _ in range(Q):
    x, y = map(int, input().split())
    prev = A[x - 1]
    A[x - 1] = y
    c = gar_set.discard(prev)

    # gar_setからprevを取り出しているとき
    if c:
        if use_set.lt(y) is not None:
            el = use_set.ge(-1)
            use_set.discard(el)
            gar_set.add(el)
            tot -= el
            use_set.add(y)
            tot += y
        else:
            gar_set.add(y)

    # gar_set内にprevがなかったとき
    else:
        use_set.discard(prev)
        tot -= prev
        if use_set.le(y) is None:
            gar_set.add(y)
            el = gar_set.le(1000000007)
            gar_set.discard(el)
            use_set.add(el)
            tot += el
        else:
            use_set.add(y)
            tot += y
    print(tot)

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1