EAFP

~ Easier to Ask for Forgiveness than Permission ~

Atcoder-ABC218をpythonで解いてみた

目的

  • Atcoderで上を目指すべく頑張っている中でABC218を解いたので、解説してみる
    • pypy7.3を使って解いている

A問題

atcoder.jp

考察

  • 問題文通りに解けば良い

実装

N = int(input())
S = input()

if S[N-1] == "o":
    print("Yes")
else:
    print("No")

結果

B問題

atcoder.jp

考察

  • 問題文通りに解けば良い
  • ACSIIコードを利用する(a=97, b=98, ... となる)

qiita.com

実装

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

ans = []

for i in range(len(P)):
    ans.append(chr(P[i]+96))

print(*ans,sep="")

結果

C問題

atcoder.jp

考察

  • 各図形をトリミングして考えれば良さそう
  • 平行移動に関しては、トリミング結果と同一であれば良い
  • 90度回転を3度行い、基の図形と同様になるかを確認する

  • トリミングは、図形の左上詰で考える

    • # が含むx, y座標の最小値、最大値をそれぞれ取得し、座標を変換する

f:id:kazu_0716:20210916012344p:plain
トリミングのイメージ

実装

提出したコードをリファクタしたもの

N = int(input())
S, T = [], []


def set_points(l):
    min_x, max_x, min_y, max_y = 300, -1, 300, -1
    for i in range(N):
        s = list(input())
        for j in range(len(s)):
            if s[j] == "#":
                l.append([j, i])
                min_x = min(min_x, j)
                max_x = max(max_x, j)
                min_y = min(min_y, i)
                max_y = max(max_y, i)
    return min_x, max_x, min_y, max_y


def convert(l, min_x, max_x, min_y, max_y):
    shape = [[0] * (max_x - min_x + 1)
             for _ in range((max_y - min_y + 1))]
    for x, y in l:
        x -= min_x
        y -= min_y
        shape[y][x] = 1
    return shape


min_xs, max_xs, min_ys, max_ys = set_points(S)
min_xt, max_xt, min_yt, max_yt = set_points(T)

s = convert(S, min_xs, max_xs, min_ys, max_ys)
t = convert(T, min_xt, max_xt, min_yt, max_yt)

flag = False
for _ in range(4):
    if s == t:
        flag = True
        break
    s = list(map(list, zip(*s[::-1])))

if flag:
    print("Yes")
else:
    print("No")

結果

D問題

atcoder.jp

考察

解説では、対角線上の2点を求めていたが、別のアプローチを取った

  • 長方形の左下の点(P1)を定める
  • P1のx座標の中のどれかが、同じ点がP2となる
  • P1のy座標の中のどれかが、同じ点がP3となる
  • P4 = (P3x, P2y)が、存在するか確認し、あればcountする

f:id:kazu_0716:20210916013306p:plain
ABC218 Dのメモ

実装

from itertools import combinations

N = int(input())
X, Y = {}, {}

ans = 0

for _ in range(N):
    x, y = map(int, input().split())
    if x in X:
        X[x].add((x, y))
    else:
        X[x] = set([(x, y)])
    if y in Y:
        Y[y].add((x, y))
    else:
        Y[y] = set([(x, y)])

for x in X:
    xset = X[x]
    for p1, p2 in combinations(xset, 2):
        yset = Y[p1[1]]
        yset -= set([p1, p2])
        for p3 in yset:
            if (p3[0], p2[1]) in Y[p2[1]]:
                ans += 1

print(ans)

結果

E問題

atcoder.jp

考察

本番中は時間なくて解けなかった

  • 最小全域木問題であり、クラスカル法で解くことを考える
  • UnionFind-Treeを使うことは分かったが、どのように結合していくかが分からなかった

dai1741.github.io

  • 今回は、コストの小さい順に各nodeをUninFind-Treeで結合していく
    • 経路がマイナスなものは、結合する
    • それぞれのnodeの親が違う場合も結合する
    • それ以外の場合は、edgeを切っても良いので、コストとして加算する
      • 結合しない

実装

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def same(self, x, y):
        return self.find(x) == self.find(y)


N, M = map(int, input().split())
uf = UnionFind(N)

edges = [tuple(map(int, input().split())) for _ in range(M)]
edges.sort(key=lambda x: x[2])
ans = 0

for a, b, c in edges:
    if c < 0 or not uf.same(a-1, b-1):
        uf.union(a-1, b-1)
    else:
        ans += c

print(ans)

結果

まとめ

  • 今回は、D問題まで解けた!次も頑張るぞ!
    • 人生2度目のD問題解くのに成功し、非常に嬉しい

f:id:kazu_0716:20210916014044p:plain
ABC218の結果

  • E問題は最小全域木問題で、典型問題だったが、勉強不足で解けなかった
    • 今回クラスカル法も勉強でき、UninonFindの使い方も学べてよかった(目的に応じ、特定順序でsortしたのちに、結合していく)