こんにちは、こんぶちゃ(茶コーダー)です!
初心者視点から競プロAtCoderのABC309のA問題〜C問題の自身の解答と解答説明をしていきます。
まだまだ学習の身ですので、最適コードではない部分も多々あるかと思いますが、その部分は同じ学習者として温かい気持ちで応援&ご助言頂けば嬉しいです。
では早速行ってみましょう!
今回の参加コンテスト(ABC309)
A問題:Nine
問題(要約)
1から9までの数字が書かれた 3 x 3 の盤面が与えられる。左上から右上、次の行・・・、右下の順。与えられた A, B が左右で隣接しているかを判断する。ただし A < B の関係が成り立つ
解答
# 番号1
a,b = map(int,input().split())
# 番号2
if a + 1 != b:
exit(print("No"))
# 番号3
if a==3 or a==6:
exit(print("No"))
# 番号4
print("Yes")
考え方 (下の番号は、コード内のコメント番号と連携している)
A < B の条件が成り立つため、隣り合う条件としては下記になる
- A, Bが連番になっている
- A がマス目の端にこない
ただし、A<Bの関係から、Aが9をとることはない
上記の条件に当てはまる場合は、”No” を出力し、それ以外の場合は、”Yes” を出力する
以下、プログラム内容詳細
- 入力a, bを受け取る。map関数を用いて整数化している
- AとBの数が連番になっていないか確認、つまり、A+1 と B が等しくないかを確認する
- 等しくない場合は、左右隣同士になることがないため、”No”を出力しプログラムを終了させる
- A がマス目の端に配置されているか確認、つまり、A が 3, 6 と等しいかを確認する
- Aが3, 6 と等しい場合は、左右隣同士になることがないため、”No”を出力しプログラムを終了させる
- 途中でプログラムが終了しなければ、問題の条件を満たすため、”Yes”を出力させる
B問題:Rotate
問題(要約)
N行N列のマス目が与えられる。マス目の外側に書かれた整数を時計回りに1個ずらした時のマス目を出力する
解答
# 番号1
n=int(input())
A=[list(input()) for _ in range(n)]
# 番号2
outers=[]
# 番号3
# 番号3-1
for j in range(n):
outers.append(A[0][j])
# 番号3-2
for i in range(1,n):
outers.append(A[i][n-1])
# 番号3-3
for j in range(n-2,-1,-1):
outers.append(A[n-1][j])
# 番号3-4
for i in range(n-2,0,-1):
outers.append(A[i][0])
# 番号4
shiftedOuters = [outers[-1]] + outers[:-1]
# 番号5
cnt=0
# 番号6
# 番号6-1
for j in range(n):
A[0][j]=shiftedOuters[cnt]
cnt+=1
# 番号6-2
for i in range(1,n):
A[i][n-1]=shiftedOuters[cnt]
cnt+=1
# 番号6-3
for j in range(n-2,-1,-1):
A[n-1][j]=shiftedOuters[cnt]
cnt+=1
# 番号6-4
for i in range(n-2,0,-1):
A[i][0]=shiftedOuters[cnt]
cnt+=1
# 番号7
for i in range(n):
for j in range(n):
print(A[i][j],end="")
print()
考え方(下の番号は、コード内のコメント番号と連携している)
周囲の要素のみの配列を作成 -> 作成した配列を一つずらす -> 配列Aの周囲にずらした配列を適用する
- 入力 n, A を受け取る
- nは整数型に変換する
- Aは文字列のまま受け取り配列に変換する
* 数字としての意味がなく、文字列のままの方が計算されずに扱いやすい- 内包表記を用いて、N行分をリスト形式で取り込む
- 周囲の要素を格納しておく配列outersを用意する
- 周囲の要素を配列outersに格納していく。左上 -> 右上 -> 右下 -> 左下 -> 左上 の順番で登録していく
- 左上 -> 右上: 各要素をappendでoutersに格納してく
- 右上 -> 右下: A[0][n-1] の要素は 番号3-1 で格納済みなので、i は1〜始める
- 右下 -> 左下: A[n-1][n-1] の要素は 番号3-2 で格納済み。
j は n-2 から -1 ずつしていく必要がある
* rangeの範囲に注意が必要。降順で0までの場合は、-1に設定する - 左下 -> 左上:A[n-1][0] の要素は 番号3-3 で格納済み。
また、A[0][0] の要素は 番号3-1 で格納済み。
こちらもrangeの範囲に気をつけながら範囲を設定する - 注意:P0 は、配列Dに含まれない場合なので、P0の場合は辞書型に登録しない
- outersの要素を1つ右にシフトし、shiftedOutersを作成する
- スライサを用いて最後の要素を1番目、残りを後ろに組み合わせる
* 要素一つの場合は、配列でなくなるために [ ] をつけることにより、一つでも配列化する
(配列同士の組み合わせを行うために行なっている)
- スライサを用いて最後の要素を1番目、残りを後ろに組み合わせる
- shiftedOutersの要素を一つずつ取り出すためのcnt変数を準備する
- 番号3で要素を抽出したが、今度は配列Aの各要素を上書きしていく。
cntを +1 ずつする事により順番に取り出すことができる- 左上 -> 右上
- 右上 -> 右下
- 右下 -> 左下
- 左下 -> 左上
- 番号6で配列A自身を更新できたため、更新した配列Aを出力していく
- 要素間にスペースが入らないように、end=”” を用いている
- 行毎に改行を入れるためにprint()を用いている
C問題:Medicine
問題(要約)
N種類の薬が処方され、i 種類目の薬はai日間、毎日 bi 錠の薬を飲む必要がある。飲む薬の数が、K 錠以下になる非を求める
解答
# 番号1
from collections import defaultdict
# 番号2
drags=defaultdict(int)
total=0
# 番号3
n,k=map(int,input().split())
for _ in range(n):
a,b=map(int,input().split())
total+=b
drags[a]+=b
# 番号4
if total <= k:
exit(print(1))
# 番号5
for key,value in sorted(drags.items(), key=lambda x:x[0]):
total -= value
if total <= k:
exit(print(key+1))
考え方(下の番号は、コード内のコメント番号と連携している)
1日目の錠数の数をだし、昇順にした日付毎に錠数を減らしていく。K錠以下になった日付を出力する
以下詳細
- 辞書型のdefaultdictをインポートする
- 日付と飲む錠数、飲む錠数の数を格納する変数を準備する
- 変数dragsを辞書型で宣言する。日付aiをkey、錠数biをvalueとする
- 錠数を記憶するためのtotalを準備する
- 入力を受け取る
- n, k は整数型に変換する
- for文を用いて a, b を受け取る
- a, b を整数型に変換する
- 錠数をtotalに加える事により、1日目の飲む錠数の数を求める
- 変数dragsに日付と錠数を記憶させる
- 同じ日付で複数の錠剤を飲む可能性もあるため、錠数は加算方式を採用する
- totalの数が k 錠以下の場合を判断する
- これが成り立てば、1日目でK錠以下を達成しているため1を出力させる
- dragsに登録されている日付と錠数をfor文を用いて確認する
- sorted関数を用いる事により、薬を飲み終わるのが早い順に並び替える
- .items()を用いる事により、各項目のkey, valueを指定できるようにする
- 並び替えのkeyにlambda関数を用いて、日付にアクセルをする
- totalの数から飲み終わった錠数を減算する
- 減算した値がk錠以下可動かを判断する
- k錠以下の場合は、日付(カウンタ変数 key)を出力する
- 飲んでいる錠数の最後の日が日付になるため、key + 1 を出力する
- sorted関数を用いる事により、薬を飲み終わるのが早い順に並び替える
補足:辞書型のdictとdict.items()の違い
辞書型dictをdict.items()で用いる時の違いを下記でまとめてもらいました
違い | dict | dict.items() |
---|---|---|
概要 | 辞書型オブジェクトを表す | キーと値のペアを表す |
返り値 | キーを使用して値にアクセスする | (key, value) のタプルとしてキーと値のペアを返す |
使用例 | my_dict['key'] | for key, value in my_dict.items(): |
必要な時 | 値を直接取得する場合 | キーと値のペアを両方使う場合 |
感想
今週はリアルタイム参加ができず、別で各問題を解きました。 プレッシャーがない中ですが、C問題に今週も苦戦(´・ω・`) あーでもない、こーでもない考えること半日(睡眠時間も入っています)、やっとAC解答に辿り着きました。リアルタイム参加では完全にタイムオーバーですね。とはいえ、自力で回答まで行けたことに満足しています。
最近はC問題で苦戦することが多いんですよね。たくさん問題を解いた方がいいんですけど、pythonでのvisualization(グラフ化)にはまってしまっています。グラフの連続処理などAtCoderで培ったアルゴリズムの考え方が生きてきます♪( ´▽`)
その時に興味があることを全力で行いながら、AtCoderのコンテストはマイペースに続けていこうと思います。
それでは、良い競プロライフを〜!(*^▽^)/
コメント