Learning English and Software Engineer Testing
ダイの大冒険という漫画が大好きなbun913です。
みなさんは深さ優先探索に泣いたことがありますか?私はあります。
このブログはもともと「競技プログラミングの参加メモとか壁にでも話してろよ!(意訳)」という意見を見かけて、「ほんなら自分でブログ作ったるわい!」というのもきっかけで作り始めました。
今回は、キーエンスプログラミングコンテスト2023夏(AtCoder Beginner Contest 315) - AtCoderに参加し、コンテスト中にA,B,Cの問題をコンテスト中に解くことができたので、その時の気持ちを思い返しつつ次回へのゆる反省に繋げたいと思います。
文字列から特定の文字をのぞいた結果を出力せよという類の問題ですね。
特にいうこともないのですが、配列(リスト)の方が扱いやすいかな?
というお気持ちでリストとして受け取ってからフィルターをかけて解きました。
S = list(input())
no_use = set(['a', 'i', 'u', 'e', 'o'])
f = filter(lambda x: x not in no_use, S)
print(*f, sep="")
これは計算すれば少ない計算量で解けそうだな
と思いましたが、MもDも制約が緩いので愚直に数え上げるのが間違い少なそうだな
と思いました。
ということで以下のように解くことに決めました。
このとおりに実装したら、以下のようなコードになりました。
M = int(input())
days = list(map(int, input().split()))
sum = sum(days)
# 中央の値
central = (sum+1) // 2
cur = 0
for i in range(M):
day_cnt = days[i]
month_day = 0
for cnt in range(1, day_cnt+1):
cur += 1
month_day += 1
if cur == central:
print(i+1, month_day)
アイスクリームを2個選んで、最も満足度が高くなるものを考えます。
問題文をみて、要するに同じ味のアイスから2つを選ぶ
or 違う味のアイスから2つ選ぶ
だけしかないよなぁ。と思いました。
また、同じ味のアイスから選ぶパターンでも上位2個のアイスしか使わない
から 各味のアイスの上位2種だけ保持すれば良さそう
という気づきを得ることができました。
ということで次の方針で問題を解くことにしました。
{"からあげ味": [100,99]}
のような形です最終的に以下のような実装になりました。
N = int(input())
F = []
S = []
memo = {}
for _ in range(N):
f, s = map(int, input().split())
F.append(f)
S.append(s)
if f in memo:
memo[f].append(s)
memo[f].sort(reverse=True)
# 2種だけ持つ
memo[f] = memo[f][:2]
continue
memo[f] = [s]
# 1: 同じ味から2つ選んだ場合の最大
cand1 = -1
for k, v in memo.items():
if len(v) < 2:
continue
cand1 = max(cand1, v[0] + v[1] // 2)
# 1種類しかない場合はここで終了
if len(set(F)) == 1:
print(cand1)
exit()
# 2: 違う味から2つ選んだ場合の最大値
cand2 = -1
number1_list = [lis[0] for lis in memo.values()]
number1_list.sort(reverse=True)
cand2 = number1_list[0] + number1_list[1]
print(max(cand1, cand2))
D問題をぱっとみて「うーん。これは解けないww」となったので、こちらを実装していました。
結論からいうとコンテスト中には解けずに、コンテスト終了後に解けたので、とても悔しかった問題です。
大体以下のような感じで考察し、実装方針を立てていました。
node_modules
みたいやなwwというようにテンション上がっていました。
提出してTLEになってしまったコードがこちらです。
from collections import deque
N = int(input())
parents = []
# まずグラフを作る
G = [[] for _ in range(N+1)]
for i in range(N):
C, *P = map(int, input().split())
if i == 0:
parents = P[:]
G[i+1] = P[:]
# DFSで子から親に向かってリストに詰める
# 2回目の訪問(子から親)と判別できるようにキューに必要な情報を詰めていく
# (今いるノード,親のノード,頂点の訪問回数)
q = deque([(1, 0, 1)])
ans_list = []
while q:
now, parent, cnt = q.pop()
# ここですでに答えに詰めているノードはスキップ
if now in set(ans_list):
continue
# 1回目の訪問
if cnt == 1:
q.append((now, parent, 2))
for to in G[now]:
# 注: 今回子から親への経路を保持していないので無駄実装
if to == parent:
continue
q.append((to, now, 1))
else:
for to in G[now]:
# 注: 今回子から親への経路を保持していないので無駄実装
if to == parent:
continue
# 順番を保ったまま重複を除く
# 注: 問題点
if to not in set(ans_list):
ans_list.append(to)
print(*ans_list, sep=' ')
上がTLEになってしまうコードですね。
問題点は以下の部分です。
if to not in set(ans_list):
ans_list.append(to)
すでに見た本ならスキップみたいなノリで書いているのですが、これだと計算量が多くなっています。
本来やりたいことは「答えに詰めているか」ではなく「すでに訪問しているノードか」のはずです。
ということで、すでに2回訪問しているノードかを判定するための配列を用意することで、O(1)で判定できるようになり、以下のコードでACを得ることができました。
from collections import deque
N = int(input())
# 注: いらない変数です。無駄実装です。
parents = []
# まずグラフを作る
G = [[] for _ in range(N+1)]
for i in range(N):
C, *P = map(int, input().split())
if i == 0:
parents = P[:]
G[i+1] = P[:]
# DFSで子から親に向かってリストに詰める
# 2回目の訪問(子から親)と判別できるようにキューに必要な情報を詰めていく
# (今いるノード,親のノード,頂点の訪問回数)
q = deque([(1, 0, 1)])
ans_list = []
visited = [False] * (N+1)
while q:
now, parent, cnt = q.pop()
# ここですでに答えに詰めているノードはスキップ
if visited[now]:
continue
# 1回目の訪問
if cnt == 1:
q.append((now, parent, 2))
for to in G[now]:
q.append((to, now, 1))
else:
for to in G[now]:
if visited[to]:
continue
visited[to] = True
ans_list.append(to)
print(*ans_list, sep=' ')
コンテストに出るまでのコンディションも良かったし、問題も苦手な類が少なかったのでその辺りの運もありそうです。
これからもゆるく趣味を続けたいと思います!
最後まで読んでいただいた方、本当にありがとうございました!