AtCoder Beginner Contest 197(Sponsored by Panasonic)をPythonで解く

投稿日:  更新日:2022/09/02

Atcoder Python

B!
Daniel AgreloによるPixabay(https://pixabay.com/)からの画像

こんにちは, Shinoryoです.

今回はAtCoder Beginner Contest 197(Sponsored by Panasonic)を, Pythonで解いてみた結果を書き連ねていこうと思います.

A - Rotate

入力する文字列をSとして, 求めるのはS2文字目以降の後ろにS1文字目をくっつけた, S[1:]+S[0]になります.

# 入力
S = input()
# 出力
print(S[1:]+S[0])

B - Visibility

マス目(X,Y)から上下左右4方向に順に進めていき(答えに1を足していく), 障害物#か端に突き当たったらそこで探索するのをやめればよいです. マス(X,Y)自身を含むので答えの初期値には1を加えておくこと, XYが数学の通常の座標の感覚とは違うことに注意して実装します.

B問題における探索のイメージ図 B問題における探索のイメージ. 星のマスはマス目(X,Y)を表す. 上下左右4方向に順に進めていき, 障害物#か端に突き当たったらそこで探索するのをやめる.
# 入力
H, W, X, Y = [int(x) for x in input().split()]
S = [input() for _ in range(H)]
# X, Yをindexに合わせて-1
X -= 1
Y -= 1
# 答えを格納する変数
ans = 1
# 上方向
for x in range(X-1, -1, -1):
if S[x][Y] == "#":
break
else:
ans += 1
# 下方向
for x in range(X+1, H, 1):
if S[x][Y] == "#":
break
else:
ans += 1
# 左方向
for y in range(Y-1, -1, -1):
if S[X][y] == "#":
break
else:
ans += 1
# 右方向
for y in range(Y+1, W, 1):
if S[X][y] == "#":
break
else:
ans += 1
# 出力
print(ans)

C - ORXOR

区分けの仕方は, それぞれの数の間にあると仮定する(N1)個の仕切りそれぞれに, 仕切りを置くか置かないかの2通りになるので, 2N1通りになります. それぞれの区分けの仕方は, 整数0i2N11を2進数にした表現によって表すことができます. このような全探索方法をbit全探索といいます.

下のコードでは, iを2進法で表したとき右からj番目が1ならば, A[j1]A[j]の間に仕切りがあると考えています. 右からj番目が1かどうかを確かめるには, iを2進法で表した数を右にj1だけシフト(>>演算子)し, それと1との論理積(&演算子)をとることによって確かめます.

右から$j$番目が$1$かどうかを確かめるイメージ図 右からj番目が1かどうかを確かめるイメージ. 左では, そのまま1との論理積をとることによって, 一番右が1であることを確かめている. 一方右では, 右へ1シフトしたあとで1との論理積をとることによって, 右から2番目が0であることを確かめている.

Pythonでは論理和(OR)(|演算子)と排他的論理和(XOR)(^演算子)があるので, それを用いることで計算をすることができます.

なお, 下のコードはPyPy3でACとなります. Python3だとTLEでした.

# 入力
N = int(input())
A = [int(x) for x in input().split()]
# 答えを格納する変数
# A < 2^30なので、2^30よりは小さい
ans = 1 << 30
# ループ
# 2^(N-1)回分(区間に分ける場合の数)
for i in range(1 << (N-1)):
# ored: 分けた区間内の数のビット単位ORを格納する変数
# xored: 得られた全ての値のビット単位XORを格納する変数
ored = A[0]
xored = 0
# A[1]からA[N-1]までのA[j]に関する処理
for j in range(1, N):
# 区間の分け方iの右から(j-1)番目が1(つまり、仕切りを入れる)かのチェック
if (i >> (j - 1)) & 1:
# 仕切りが入っている場合の処理
xored ^= ored # XOR演算
ored = A[j]
else:
# 仕切りが入っていない場合の処理
ored |= A[j] # OR演算
# 最後のXOR演算
xored ^= ored # XOR演算
# ansを更新
ans = min(ans, xored)
# 出力
print(ans)

D - Opposite

任意の正多角形に対してある円が存在し, その正多角形はその円に内接するようにできます.

p0pN/2の2点を結ぶ線分の中点をとることによって, 円の中心(あるいは「正多角形の中心」)qを得ることができます. そして, p0の座標とqの座標, そして正多角形が正N角形であることが分かれば, qを中心に2π/Nradだけ反時計回りにp0を回転させた点がp1であるということが分かります.

D問題を表した図図 D問題を表した図. p0pN/2の2点を結ぶ線分の中点をとることによって, 点qの座標が分かる. そして, p1は, qを中心に2π/Nradだけ反時計回りにp0を回転させた点である.

座標の回転は行列(あるいは, 複素数平面など)を用いて考えます. (x0,y0)を中心に角度αだけ反時計回りに(x,y)を回転させることを考えれば, その結果得られる(x1,y1)

(1)(x1x0y1y0)=(cosαsinαsinαcosα)(xx0yy0)

となることに注意してください. Pythonにおいて, cossinmath.cos(), math.sin()で計算できます(引数はrad). 円周率πmath.piで扱えます.

import math
# 点x、点x_0、角度(rad)を受け取って、
# 点x_0を中心にその角度だけ反時計回りに点xを回転させた座標(x_1, y_1)を返す関数
def rotate(x, x_0, angle):
x_1 = math.cos(angle)*(x[0] - x_0[0]) - math.sin(angle)*(x[1] - x_0[1]) + x_0[0]
y_1 = math.sin(angle)*(x[0] - x_0[0]) + math.cos(angle)*(x[1] - x_0[1]) + x_0[1]
return [x_1, y_1]
# 入力
N = int(input())
p_0 = [int(x) for x in input().split()]
p_N2 = [int(x) for x in input().split()]
# 正多角形の中心qを求める
q = [(p_N2[0] + p_0[0])/2, (p_N2[1] + p_0[1])/2]
# 求める点は、qを中心にp_0を(2*Pi/N)だけ反時計回りに回転させた点
p_1 = rotate(p_0, q, 2*math.pi/N)
# 出力
print(p_1[0], p_1[1])

E以降

E以降は私の能力不足故に省略いたします.

参考にしたサイト等

Search

About Me

自分の写真
理系大学生でした. Bloggerを利用して発信を行っています.

Labels

Blog Archives