roiti46's blog

主に競プロ問題の解説を載せてます

Atcoder Beginner Contest #011

ABC過去問解きその4。D問題で多少手こずったがなんとか自力で解答できた。数学得意な人のD問題の解答が理解できなくてつらい。問題はこちら

A: 来月は何月?

月が与えられるので、翌月の月を答える問題。12月の次は1月なのに注意。

print int(raw_input())%12+1

B: 名前の確認

アルファベット文字列が与えられるので、最初の文字を大文字、それ以外を小文字にして出力する問題。pythonには title() メソッドがあり、与えられた文字列の最初の文字を大文字、それ以外を小文字にしてくれる。

print raw_input().title()

C: 123引き算

ある正の整数 N が与えられる。1回の操作で1か2か3を引いていき、100回以内の操作で0にしたい。しかし3つのNG数があり、操作の途中これら数字になってしまったら失敗とみなす。それでは与えられた数字を無事0にすることができるかどうかを答える問題。

実際に与えられた数字を引いていけばいい。操作数を抑えたいので、3で引ける時は3で、だめなら2で、それもだめなら1で引いていく。途中引くことができなかったり、100回以内の操作で0にできなかったら0にすることができない場合となる。

N = int(raw_input())
NG = sorted([int(raw_input()) for _ in range(3)])
for roop in range(100):
    if N in NG:
        print "NO"
        break
    if   N-3 >= 0 and N-3 not in NG: N -= 3
    elif N-2 >= 0 and N-2 not in NG: N -= 2
    elif N-1 >= 0 and N-1 not in NG: N -= 1
    else:
        print "NO"
        break
    if N == 0:
        print "YES"
        break
else:
    print "NO"

D: 大ジャンプ

あなたははじめ (0,0) にいるが N 回のジャンプで (X,Y) に移動したい。ただし、ジャンプを一回行うとつぎの4つの移動のどれか1つがランダムで選ばれる。

  • X 軸に平行に +D だけ移動する。
  • X 軸に平行に −D だけ移動する。
  • Y 軸に平行に +D だけ移動する。
  • Y 軸に平行に −D だけ移動する。

それでは N 回のジャンプ後にあなたが (0,0) から (X,Y) に移動できている確率はいくらか、という問題。 十字路が張り巡らされた街で、ある場所から目的の場所への行き方は何通りありますか?という問題の拡張版のようなもの。

自分のやり方は次のような感じ。それぞれの軸の+とーのジャンプ数を探索し、ジャンプ数の合計が N に合うとき、 ansにそれらの並び替えの組み合わせ数を足しあわせていく。最後に4Nで割って確率を求めるというもの。

このやり方はあまり賢いやり方ではないように思える。最後に4で割っていくとき、最初ansは超長大整数の場合があり、そのまま4.0で割ったり1.0を掛けたりしてfloatに直そうとすると "long int too large to convert to float" というOverflowエラーになってしまう。そこで求められる精度である10^-9を実現できる10桁程度まで整数による割り算を行い、そのあと実数に変換して割り算を継続している。はじめてこういうエラーが出たので戸惑ったが、テストケースの名称が条件そのままだったのでデバッグが楽に出来た。ありがたい。

fact = {i:0 for i in range(1001)}
# 階乗を返す。一度計算した値は保存しておく。
def f(n):
    res,nn = 1,n
    while n > 0:
        if fact[n] > 0:
            res *= fact[n]
            return res
        res *= n
        n -= 1
    fact[nn] = res
    return res
 
N,D = map(int,raw_input().split())
X,Y = map(int,raw_input().split())
ans = 0
for px in xrange(N+1):
    if px*D-X < 0 or (px*D-X)%D != 0: continue
    dx = (px*D-X)/D
    for py in xrange(N-px-dx+1):
        if py*D-Y < 0 or (py*D-Y)%D != 0: continue
        dy = (py*D-Y)/D
        if px+dx+py+dy == N:
            # 矢印の並び替えを思い描いてもらえばわかりやすいと思います
            ans += f(N)/f(px)/f(dx)/f(py)/f(dy)
 
for i in range(N):
    if len(str(ans)) > 14: ans /= 4
    else: ans /= 4.0
print "%.15f"%ans