きなこの精進日記[python]

ABC184バチャ

A - Determinant

概要

行列が与えられ、行列式を求める

カテゴリ

算数(四則演算)

考察

入力全て整数で、桁も小さいので特に型などは気にしなくていい
a*d-b*c

コード

#!/usr/bin/env python3
a, b = map(int, input().split())
c, d = map(int, input().split())
print(a * d - b * c)

B - Quizzes

概要

最初の持ち点がNでクイズの正解、不正解履歴が文字列で与えられる。
持ち点は正解、不正解に応じて変動し、oなら+1,xなら-1をする。(ただし、持ち点が負になることはない)
最終的な点数を答えよ

カテゴリ

ゲーム(そのままルールを実装)

考察

ループで文字列の長さ分を回す

コード

#!/usr/bin/env python3
n, x = map(int, input().split())
s = input()
cur_point = x
for i in s:
    if cur_point == 0 and i == "x":
        pass
    elif i == "x":
        cur_point -= 1
    else:
        cur_point += 1

print(cur_point)

C - Super Ryuma

概要

無限に広がる二次元グリッド上の位置(r1,c1)にコマがあり、それを(r2,c2)に動かすための最小手順を答えよ。
ただし、一回の移動で(a,b)にいるコマは以下の条件のどちらかを満たす場所(c,d)に動かせる。

 A:ユークリッド距離が3以下
 B:対角の位置(将棋の角が移動できる場所)

カテゴリ

最短経路、グリッド

考察

可能な2つの動き(斜めと3マス以内の移動)を考えた時に、2つの動きの順番は関係ない。
つまり、A,A,B でたどり着くならばB,A,Aでも同じ場所にたどり着く。

そのため、Bの動きを行う回数を固定してk回行うとすると、Aの動きを何回行えば終了時の位置とゴール位置のユークリッド距離を3*k 以内にできるか、という問題になる。

ただし、これではk に対してO(10**9)かかるので、明らかに間に合わない。
どういう時にAの動きがBよりも優位になるかを考えると、移動後にゴールとのユークリッド距離が3よりも減っている時、ということになる。

ここで、Aの動きには向きが2つあり(右斜と左斜)、その2つを組み合わせることで一回の動きではユークリッド距離が縮まらない場合にも2回トータルすると大きく縮められることがわかる(例として、スタートとゴール位置のx座標が同じ時など)

さらに、A,Bの動きの順番が関係ないことを考えると、Aの2つの動きはそれぞれ高々一回でよくて、ゴール地点から垂線を引いてそれにぶつかるまでAの動きをすればAの動きだけで最も近づくことができる。
それを行った時の距離は1か0なので手数は3,2,1,0のいずれかになる

実装

手数が0,1,2,3になる条件を羅列していって、手数を確定させる(簡単な少ない手数の条件から記述して3はその余事象とする)

移動A二回でゴールにつく条件は必ずどちらの動作もa-c,b-dの差が偶数変化するので、abs(a-c) + abs(b-d) % 2 == 0 で、
A一回、B一回でゴールにつけるかの判定は、abs(abs(a - c) - abs(b - d)) <= 3 で行える(気づくのにだいぶ時間がかかった)

#!/usr/bin/env python3
a, b = map(int, input().split())
c, d = map(int, input().split())
if a == c and b == d:
    print(0)
elif abs(a - c) == abs(b - d) or abs(a - c) + abs(b - d) <= 3:
    print(1)
else:
    if (
        abs(a - c) + abs(b - d) <= 6
        or (abs(a - c) + abs(b - d)) % 2 == 0
        or abs(abs(a - c) - abs(b - d)) <= 3
    ):
        print(2)
    else:
        print(3)

D - increment of coins

概要

ゲームの期待値を求める

カテゴリ

ゲーム、期待値

考察

ゲームが終了するまでの手数の期待値を求める問題
余事象で考えられるか考えたが、無理なので、全状態を列挙する必要があると考える。
ただし、全てのpath(どれかが100枚になるまで)を考えると状態数が多くなりすぎるので、DPのように状態をまとめることを考える。(それぞれがx,y,z になる確率はx-1,y,z,とx,y-1,z , x,y,z-1から移動する確率の全ての和で計算できる)

計算量はO(10**6)(DPが10**6マスあって、それぞれの遷移がO(1)*3パス)なので、これで間に合う。

実装

期待値DPなのでゴールから埋めていく。
DP[ある状態] = (そこからゴールまでの期待値) という形で漸化式を形成する

#!/usr/bin/env python3
# input
a, b, c = map(int, input().split())
dp = [[[0] * 103 for i in range(103)] for j in range(103)]
dp[a][b][c] = 1  # initialization
for i in range(1, 300 - a - b - c + 1):  # スタートからの距離
    for a_d in range(i + 1):  # aに割り当てる距離
        for b_d in range(i + 1 - a_d):
            c_d = i - a_d - b_d
            if (((a + a_d) == 100) + ((b + b_d) == 100) + ((c + c_d) == 100)) > 1:
                continue
            elif a + a_d > 100 or b + b_d > 100 or c + c_d > 100:
                continue
            else:
                if a + a_d > 0 and b + b_d < 100 and c + c_d < 100:
                    dp[a + a_d][b + b_d][c + c_d] += dp[a + a_d - 1][b + b_d][
                        c + c_d
                    ] * ((a + a_d - 1) / (a + b + c + i - 1))
                if b + b_d > 0 and a + a_d < 100 and c + c_d < 100:
                    dp[a + a_d][b + b_d][c + c_d] += dp[a + a_d][b + b_d - 1][
                        c + c_d
                    ] * ((b + b_d - 1) / (a + b + c + i - 1))
                if c + c_d > 0 and b + b_d < 100 and a + a_d < 100:
                    dp[a + a_d][b + b_d][c + c_d] += dp[a + a_d][b + b_d][
                        c + c_d - 1
                    ] * ((c + c_d - 1) / (a + b + c + i - 1))
# print(dp[99][99][99:], dp[100][99][98:], dp[99][100][98:])
ans = 0
for i in range(101):
    for j in range(101):
        for k in range(101):
            if i == 100 or j == 100 or k == 100:
                ans += dp[i][j][k] * (i + j + k - a - b - c)
print(ans)