結果

問題 No.463 魔法使いのすごろく🎲
ユーザー rpy3cpp
提出日時 2017-04-18 23:24:03
言語 Python3
(3.8.2 + numpy 1.14.5 + scipy 1.1.0)
結果
AC  
実行時間 83 ms / 2,000 ms
コード長 4,308 Byte
コンパイル時間 49 ms
使用メモリ 8,948 KB
最終ジャッジ日時 2020-05-11 23:09:14

テストケース

テストケース表示
入力 結果 実行時間
使用メモリ
testcase_00 AC 23 ms
8,944 KB
testcase_01 AC 22 ms
6,896 KB
testcase_02 AC 22 ms
8,900 KB
testcase_03 AC 20 ms
6,896 KB
testcase_04 AC 25 ms
8,940 KB
testcase_05 AC 34 ms
8,940 KB
testcase_06 AC 20 ms
8,948 KB
testcase_07 AC 21 ms
8,944 KB
testcase_08 AC 21 ms
8,944 KB
testcase_09 AC 72 ms
8,944 KB
testcase_10 AC 20 ms
8,900 KB
testcase_11 AC 31 ms
8,940 KB
testcase_12 AC 25 ms
6,900 KB
testcase_13 AC 20 ms
8,944 KB
testcase_14 AC 67 ms
6,900 KB
testcase_15 AC 50 ms
6,896 KB
testcase_16 AC 20 ms
8,940 KB
testcase_17 AC 60 ms
8,900 KB
testcase_18 AC 19 ms
8,944 KB
testcase_19 AC 20 ms
8,896 KB
testcase_20 AC 20 ms
6,900 KB
testcase_21 AC 82 ms
6,900 KB
testcase_22 AC 83 ms
6,940 KB
testcase_23 AC 83 ms
8,944 KB
testcase_24 AC 79 ms
8,940 KB
testcase_25 AC 83 ms
8,948 KB
testcase_26 AC 80 ms
8,948 KB
testcase_27 AC 80 ms
8,940 KB
testcase_28 AC 82 ms
8,944 KB
testcase_29 AC 81 ms
8,944 KB
testcase_30 AC 80 ms
6,904 KB
testcase_31 AC 79 ms
6,900 KB
testcase_32 AC 81 ms
8,940 KB
testcase_33 AC 81 ms
6,904 KB
testcase_34 AC 80 ms
8,944 KB
testcase_35 AC 19 ms
8,944 KB
testcase_36 AC 20 ms
8,900 KB
testcase_37 AC 20 ms
6,900 KB
testcase_38 AC 19 ms
6,896 KB
権限があれば一括ダウンロードができます

ソースコード

diff #
import copy

def read_data():
    n, m = map(int, input().split())
    if n == 2 and m == 1:
        Cs = [0, 0]
    else:
        Cs = [0] + list(map(int, input().split())) + [0]
    return n, m, Cs

def solve(n, m, Cs):
    '''
    dp1[a]: 位置 a から魔法を使わずにゴールするときのコストの期待値
    dp1[a] = sum(dp1[c] + Cs[c] for b in range(a + 1, a + m + 1))/m
    ただし、 c = b if b < n else 2 * n - 2 - b
    
    A = (1/m) *[[0, 1, 1, 1, ..., 1, 0, 0, 0, 0, 0],
         [0, 0, 1, 1, ..., 1, 1, 0, 0, 0, 0],
         [0, 0, 0, 1, ..., 1, 1, 1, 0, 0, 0],
         ...
         [0, 0, 0, 0, ..., 1, 1, 1, 1, 1, 0],
         [0, 0, 0, 0, ..., 0, 1, 1, 1, 1, 1],
         [0, 0, 0, 0, ..., 0, 0, 1, 1, 2, 1],
         [0, 0, 0, 0, ..., 0, 0, 0, 2, 2, 1],
         [0, 0, 0, 0, ..., 0, 0, 1, 1, 2, 1],
         [0, 0, 0, 0, ..., 0, 1, 1, 1, 1, 1],
         [0, 0, 0, 0, ..., 0, 0, 0, 0, 0, 0]]
    とすると、
    dp1 = A dp1 + A Cs
    <=> (I - A)dp1 = A Cs = (A - I) Cs + Cs = - (I - A)Cs + Cs
    <=> dp1 = -(I - A)^-1 (I - A) Cs + (I - A)^-1 Cs
            = - Cs + (I - A)^-1 Cs
    <=> dp1 + Cs = (I - A)^-1 Cs
    
    dp2 = dp1 + Cs とする。
    
    dp0[a]: 位置 a からゴールするまでのコストの期待値の最小値(魔法を1度使ってもよい)
    dp0[a] = min(min(dp1[b] + Cs[b] for b in range(a + 1, min(a + m + 1, n))),
                 sum(dp0[c] + Cs[c] for b in range(a + 1, a + m + 1))/m)
    ただし、 c = b if b < n else 2 * n - 2 - b
    
    dp0[n - 1] = 0      # ゴールしている。
    dp0[n - 2] = 0      # 魔法で 1 を出せば、ゴールできる。
    dp0[n - 3] = 0      # 魔法で 2 を出せば、ゴールできる。
    ...
    dp0[n - m - 1] = 0  # 魔法で m を出せば、ゴールできる。
    dp0[n - m - 2] = min(dp2[b] for b in range(n - m - 2 + 1, n - 1)),
                         sum(dp0[b] + Cs[b] for b in range(n - m - 2 + 1, n - m - 2 + m + 1))/m)
    dp0[a] = min(dp2[b] for b in range(a + 1, a + m + 1)),
                 sum(dp0[b] + Cs[b] for b in range(a + 1, a + m + 1))/m)
    '''
    A = calc_matrix_A(n, m)
    I = identity_matrix(n)
    ImA = mat_minus(I, A)
    colCs = [[c] for c in Cs]
    det, dp2 = gauss(ImA, colCs)
    dp2 = [a[0] for a in dp2]
    dp0 = [0] * n
    for a in range(n - m - 2, -1, -1):
        min_dp2 = min(dp2[a + 1:a + m + 1])
        avg_dp0 = sum(dp0[b] + Cs[b] for b in range(a + 1, a + m + 1))/m
        dp0[a] = min(min_dp2, avg_dp0)
    return dp0[0]
    
def calc_matrix_A(n, m):
    A = [[0] * n for _ in range(n)]
    for r, row in enumerate(A):
        if r == n - 1:
            break
        for c in range(r + 1, r + 1 + m):
            row[b2c(c, n)] += 1/m
    return A

def identity_matrix(n):
    I = [[0] * n for _ in range(n)]
    for i in range(n):
        I[i][i] = 1
    return I

def mat_minus(A, B):
    C = [[a - b for a, b in zip(rowA, rowB)] for rowA, rowB in zip(A, B)]
    return C

def b2c(b, n):
    if b < n:
        return b
    else:
        return 2 * n - 2 - b

def gauss(a, b):
    '''The 'gauss' function takes two matrices, 'a' and 'b', with 'a' square, and it return the determinant of 'a' and a matrix 'x' such that a*x = b.
    If 'b' is the identity, then 'x' is the inverse of 'a'.
    Source: Rosetta Code
    http://rosettacode.org/wiki/Gaussian_elimination#Python
    '''
    a = copy.deepcopy(a)
    b = copy.deepcopy(b)
    n = len(a)
    p = len(b[0])
    det = 1
    for i in range(n - 1):
        k = i
        for j in range(i + 1, n):
            if abs(a[j][i]) > abs(a[k][i]):
                k = j
        if k != i:
            a[i], a[k] = a[k], a[i]
            b[i], b[k] = b[k], b[i]
            det = -det

        for j in range(i + 1, n):
            t = a[j][i]/a[i][i]
            for k in range(i + 1, n):
                a[j][k] -= t*a[i][k]
            for k in range(p):
                b[j][k] -= t*b[i][k]

    for i in range(n - 1, -1, -1):
        for j in range(i + 1, n):
            t = a[i][j]
            for k in range(p):
                b[i][k] -= t*b[j][k]
        t = 1/a[i][i]
        det *= a[i][i]
        for j in range(p):
            b[i][j] *= t
    return det, b


if __name__ == '__main__':
    n, m, Cs = read_data()
    print(solve(n, m, Cs))
0