提出 #76871380


ソースコード 拡げる

import sys
from collections import deque
import time

def solve():
    input = sys.stdin.readline
    N, M, K = map(int, input().split())
    grid = [input().strip() for _ in range(N)]

    door_h = [[-1] * N for _ in range(N - 1)]
    door_v = [[-1] * (N - 1) for _ in range(N)]
    switch = [[-1] * N for _ in range(N)]
    switch_pos = [None] * K
    used_switch = [False] * K
    doors_placed = 0

    def is_open(g, mask):
        if g == -1:
            return True
        k = g // 2
        return ((mask >> k) & 1) == (g & 1)

    # 完全BFS(Tを返す)
    def calc_T():
        INF = -1
        total = N * N
        size = (1 << K) * total
        dist = [INF] * size
        dist[0] = 0
        q = deque([0])
        while q:
            idx = q.popleft()
            d = dist[idx]
            mask = idx // total
            pos = idx % total
            i = pos // N
            j = pos % N
            if i == N - 1 and j == N - 1:
                return d
            for di, dj in ((-1,0),(1,0),(0,-1),(0,1)):
                ni, nj = i + di, j + dj
                if not (0 <= ni < N and 0 <= nj < N):
                    continue
                if grid[ni][nj] == '#':
                    continue
                if di == 1:
                    g = door_h[i][j]
                elif di == -1:
                    g = door_h[ni][nj]
                elif dj == 1:
                    g = door_v[i][j]
                else:
                    g = door_v[ni][nj]
                if not is_open(g, mask):
                    continue
                nidx = mask * total + ni * N + nj
                if dist[nidx] == INF:
                    dist[nidx] = d + 1
                    q.append(nidx)
            s = switch[i][j]
            if s != -1:
                nmask = mask ^ (1 << s)
                nidx = nmask * total + i * N + j
                if dist[nidx] == INF:
                    dist[nidx] = d + 1
                    q.append(nidx)
        return 0

    # 経路復元つきBFS
    def calc_T_with_path():
        INF = -1
        total = N * N
        size = (1 << K) * total
        dist = [INF] * size
        prev = [(-1, -1, -1)] * size
        dist[0] = 0
        q = deque([0])
        while q:
            idx = q.popleft()
            d = dist[idx]
            mask = idx // total
            pos = idx % total
            i = pos // N
            j = pos % N
            if i == N - 1 and j == N - 1:
                path = []
                cur = idx
                while cur != -1:
                    cm = cur // total
                    cp = cur % total
                    ci = cp // N
                    cj = cp % N
                    path.append((cm, ci, cj))
                    p = prev[cur]
                    if p[0] == -1:
                        break
                    cur = p[0] * total + p[1] * N + p[2]
                path.reverse()
                return d, path
            for di, dj in ((-1,0),(1,0),(0,-1),(0,1)):
                ni, nj = i + di, j + dj
                if not (0 <= ni < N and 0 <= nj < N):
                    continue
                if grid[ni][nj] == '#':
                    continue
                if di == 1:
                    g = door_h[i][j]
                elif di == -1:
                    g = door_h[ni][nj]
                elif dj == 1:
                    g = door_v[i][j]
                else:
                    g = door_v[ni][nj]
                if not is_open(g, mask):
                    continue
                nidx = mask * total + ni * N + nj
                if dist[nidx] == INF:
                    dist[nidx] = d + 1
                    prev[nidx] = (mask, i, j)
                    q.append(nidx)
            s = switch[i][j]
            if s != -1:
                nmask = mask ^ (1 << s)
                nidx = nmask * total + i * N + j
                if dist[nidx] == INF:
                    dist[nidx] = d + 1
                    prev[nidx] = (mask, i, j)
                    q.append(nidx)
        return 0, []

    # 固定マスクの到達可能範囲(スイッチ操作なし)
    def bfs_reachable(start_mask, start_i, start_j, blocked_edge=None):
        dist = [[-1] * N for _ in range(N)]
        dist[start_i][start_j] = 0
        q = deque([(start_i, start_j)])
        while q:
            i, j = q.popleft()
            d = dist[i][j]
            for di, dj in ((-1,0),(1,0),(0,-1),(0,1)):
                ni, nj = i + di, j + dj
                if not (0 <= ni < N and 0 <= nj < N):
                    continue
                if grid[ni][nj] == '#':
                    continue
                if blocked_edge:
                    bi1, bj1, bi2, bj2 = blocked_edge
                    if (i==bi1 and j==bj1 and ni==bi2 and nj==bj2) or (i==bi2 and j==bj2 and ni==bi1 and nj==bj1):
                        continue
                if di == 1:
                    g = door_h[i][j]
                elif di == -1:
                    g = door_h[ni][nj]
                elif dj == 1:
                    g = door_v[i][j]
                else:
                    g = door_v[ni][nj]
                if not is_open(g, start_mask):
                    continue
                if dist[ni][nj] == -1:
                    dist[ni][nj] = d + 1
                    q.append((ni, nj))
        return dist

    start_time = time.time()
    TIME_LIMIT = 1.8  # 安全マージン

    # ----- メイン貪欲 -----
    current_T, current_path = calc_T_with_path()

    for k in range(K):
        if time.time() - start_time > TIME_LIMIT:
            break
        improved = True
        while improved and doors_placed < M:
            if time.time() - start_time > TIME_LIMIT:
                break
            if not current_path:
                break

            # 経路上の辺を列挙(最大50本)
            edges = []
            prev = current_path[0]
            for state in current_path[1:]:
                if state[1] != prev[1] or state[2] != prev[2]:
                    if state[1] == prev[1]:  # 横移動
                        jj = min(state[2], prev[2])
                        edges.append(('h', prev[1], jj, prev[0], prev[1], prev[2]))
                    else:  # 縦移動
                        ii = min(state[1], prev[1])
                        edges.append(('v', ii, prev[2], prev[0], prev[1], prev[2]))
                prev = state

            best_gain = 0
            best_door = None
            best_sw_pos = None

            seen = set()
            for edge in edges[:50]:  # 最初の50本だけ試す
                typ, ei, ej, emask, fi, fj = edge
                if typ == 'v':
                    if door_h[ei][ej] != -1:
                        continue
                    key = ('v', ei, ej)
                    blocked = (ei, ej, ei+1, ej)
                else:
                    if door_v[ei][ej] != -1:
                        continue
                    key = ('h', ei, ej)
                    blocked = (ei, ej, ei, ej+1)
                if key in seen:
                    continue
                seen.add(key)

                # スイッチ位置候補(最遠の空きマス)
                reach = bfs_reachable(emask, fi, fj, blocked)
                cand_sw = None
                max_d = -1
                for si in range(N):
                    for sj in range(N):
                        if reach[si][sj] != -1 and switch[si][sj] == -1 and grid[si][sj] == '.':
                            d = reach[si][sj]
                            if d > max_d:
                                max_d = d
                                cand_sw = (si, sj)
                if cand_sw is None:
                    continue

                # 仮設置
                if typ == 'v':
                    door_h[ei][ej] = 2 * k + 1
                else:
                    door_v[ei][ej] = 2 * k + 1
                si, sj = cand_sw
                old_sw_val = switch[si][sj]
                switch[si][sj] = k
                if not used_switch[k]:
                    # 未使用ならスイッチ位置を仮更新
                    old_sw_pos = switch_pos[k]
                    old_used = used_switch[k]

                new_T = calc_T()
                gain = new_T - current_T

                # 元に戻す
                if typ == 'v':
                    door_h[ei][ej] = -1
                else:
                    door_v[ei][ej] = -1
                if old_sw_val != -1:
                    switch[si][sj] = old_sw_val
                else:
                    switch[si][sj] = -1
                if not used_switch[k]:
                    switch_pos[k] = old_sw_pos
                    used_switch[k] = old_used

                if gain > best_gain:
                    best_gain = gain
                    best_door = (typ, ei, ej)
                    best_sw_pos = cand_sw

            if best_gain > 0 and best_door is not None:
                # 設置確定
                typ, ei, ej = best_door
                if typ == 'v':
                    door_h[ei][ej] = 2 * k + 1
                else:
                    door_v[ei][ej] = 2 * k + 1
                si, sj = best_sw_pos
                if not used_switch[k]:
                    switch[si][sj] = k
                    switch_pos[k] = (si, sj)
                    used_switch[k] = True
                else:
                    # 既存のスイッチをより遠くへ移動
                    old_si, old_sj = switch_pos[k]
                    switch[old_si][old_sj] = -1
                    switch[si][sj] = k
                    switch_pos[k] = (si, sj)
                doors_placed += 1
                current_T, current_path = calc_T_with_path()
                improved = True
            else:
                # このスイッチ種類では改善不可
                if not used_switch[k]:
                    # スイッチだけでも設置(入口から最遠)
                    best_sw = None
                    max_d = -1
                    for si in range(N):
                        for sj in range(N):
                            if grid[si][sj] == '.' and switch[si][sj] == -1:
                                d = bfs_reachable(0,0,0)[si][sj]
                                if d > max_d and d != -1:
                                    max_d = d
                                    best_sw = (si, sj)
                    if best_sw:
                        si, sj = best_sw
                        switch[si][sj] = k
                        switch_pos[k] = (si, sj)
                        used_switch[k] = True
                break

    # ----- 出力 -----
    doors = []
    for i in range(N-1):
        for j in range(N):
            if door_h[i][j] != -1:
                doors.append((0, i, j, door_h[i][j]))
    for i in range(N):
        for j in range(N-1):
            if door_v[i][j] != -1:
                doors.append((1, i, j, door_v[i][j]))
    print(len(doors))
    for d, i, j, g in doors:
        print(d, i, j, g)

    switches_out = []
    for i in range(N):
        for j in range(N):
            if switch[i][j] != -1:
                switches_out.append((i, j, switch[i][j]))
    print(len(switches_out))
    for i, j, s in switches_out:
        print(i, j, s)

if __name__ == "__main__":
    solve()

提出情報

提出日時
問題 A - Castle Renovation with Linked Doors
ユーザ Takkun2355
言語 Python (PyPy 3.11-v7.3.20)
得点 384348947
コード長 11678 Byte
結果 AC
実行時間 1988 ms
メモリ 546300 KiB

ジャッジ結果

セット名 test_ALL
得点 / 配点 384348947 / 3000000000
結果
AC × 150
セット名 テストケース
test_ALL test_0000.txt, test_0001.txt, test_0002.txt, test_0003.txt, test_0004.txt, test_0005.txt, test_0006.txt, test_0007.txt, test_0008.txt, test_0009.txt, test_0010.txt, test_0011.txt, test_0012.txt, test_0013.txt, test_0014.txt, test_0015.txt, test_0016.txt, test_0017.txt, test_0018.txt, test_0019.txt, test_0020.txt, test_0021.txt, test_0022.txt, test_0023.txt, test_0024.txt, test_0025.txt, test_0026.txt, test_0027.txt, test_0028.txt, test_0029.txt, test_0030.txt, test_0031.txt, test_0032.txt, test_0033.txt, test_0034.txt, test_0035.txt, test_0036.txt, test_0037.txt, test_0038.txt, test_0039.txt, test_0040.txt, test_0041.txt, test_0042.txt, test_0043.txt, test_0044.txt, test_0045.txt, test_0046.txt, test_0047.txt, test_0048.txt, test_0049.txt, test_0050.txt, test_0051.txt, test_0052.txt, test_0053.txt, test_0054.txt, test_0055.txt, test_0056.txt, test_0057.txt, test_0058.txt, test_0059.txt, test_0060.txt, test_0061.txt, test_0062.txt, test_0063.txt, test_0064.txt, test_0065.txt, test_0066.txt, test_0067.txt, test_0068.txt, test_0069.txt, test_0070.txt, test_0071.txt, test_0072.txt, test_0073.txt, test_0074.txt, test_0075.txt, test_0076.txt, test_0077.txt, test_0078.txt, test_0079.txt, test_0080.txt, test_0081.txt, test_0082.txt, test_0083.txt, test_0084.txt, test_0085.txt, test_0086.txt, test_0087.txt, test_0088.txt, test_0089.txt, test_0090.txt, test_0091.txt, test_0092.txt, test_0093.txt, test_0094.txt, test_0095.txt, test_0096.txt, test_0097.txt, test_0098.txt, test_0099.txt, test_0100.txt, test_0101.txt, test_0102.txt, test_0103.txt, test_0104.txt, test_0105.txt, test_0106.txt, test_0107.txt, test_0108.txt, test_0109.txt, test_0110.txt, test_0111.txt, test_0112.txt, test_0113.txt, test_0114.txt, test_0115.txt, test_0116.txt, test_0117.txt, test_0118.txt, test_0119.txt, test_0120.txt, test_0121.txt, test_0122.txt, test_0123.txt, test_0124.txt, test_0125.txt, test_0126.txt, test_0127.txt, test_0128.txt, test_0129.txt, test_0130.txt, test_0131.txt, test_0132.txt, test_0133.txt, test_0134.txt, test_0135.txt, test_0136.txt, test_0137.txt, test_0138.txt, test_0139.txt, test_0140.txt, test_0141.txt, test_0142.txt, test_0143.txt, test_0144.txt, test_0145.txt, test_0146.txt, test_0147.txt, test_0148.txt, test_0149.txt
ケース名 結果 実行時間 メモリ
test_0000.txt AC 1061 ms 518388 KiB
test_0001.txt AC 952 ms 539892 KiB
test_0002.txt AC 1120 ms 542264 KiB
test_0003.txt AC 1194 ms 539688 KiB
test_0004.txt AC 1037 ms 540804 KiB
test_0005.txt AC 1267 ms 502080 KiB
test_0006.txt AC 1552 ms 543028 KiB
test_0007.txt AC 1255 ms 545464 KiB
test_0008.txt AC 1217 ms 542964 KiB
test_0009.txt AC 829 ms 510528 KiB
test_0010.txt AC 935 ms 536448 KiB
test_0011.txt AC 1527 ms 400156 KiB
test_0012.txt AC 1912 ms 454288 KiB
test_0013.txt AC 1024 ms 542276 KiB
test_0014.txt AC 1354 ms 539704 KiB
test_0015.txt AC 1287 ms 488260 KiB
test_0016.txt AC 1514 ms 542732 KiB
test_0017.txt AC 990 ms 540372 KiB
test_0018.txt AC 911 ms 539184 KiB
test_0019.txt AC 940 ms 536884 KiB
test_0020.txt AC 1290 ms 542832 KiB
test_0021.txt AC 1219 ms 539660 KiB
test_0022.txt AC 1316 ms 539680 KiB
test_0023.txt AC 1302 ms 542832 KiB
test_0024.txt AC 1546 ms 501544 KiB
test_0025.txt AC 1627 ms 459984 KiB
test_0026.txt AC 1100 ms 539152 KiB
test_0027.txt AC 910 ms 540140 KiB
test_0028.txt AC 1666 ms 539548 KiB
test_0029.txt AC 976 ms 545524 KiB
test_0030.txt AC 1594 ms 482152 KiB
test_0031.txt AC 1209 ms 539596 KiB
test_0032.txt AC 903 ms 539676 KiB
test_0033.txt AC 1186 ms 540864 KiB
test_0034.txt AC 1073 ms 542360 KiB
test_0035.txt AC 1475 ms 539788 KiB
test_0036.txt AC 1161 ms 539600 KiB
test_0037.txt AC 1088 ms 539948 KiB
test_0038.txt AC 866 ms 539740 KiB
test_0039.txt AC 1311 ms 539696 KiB
test_0040.txt AC 1150 ms 539336 KiB
test_0041.txt AC 1152 ms 538936 KiB
test_0042.txt AC 1027 ms 459452 KiB
test_0043.txt AC 1325 ms 542884 KiB
test_0044.txt AC 1200 ms 539004 KiB
test_0045.txt AC 987 ms 536688 KiB
test_0046.txt AC 989 ms 542944 KiB
test_0047.txt AC 1473 ms 539828 KiB
test_0048.txt AC 761 ms 539120 KiB
test_0049.txt AC 1270 ms 539672 KiB
test_0050.txt AC 957 ms 542908 KiB
test_0051.txt AC 1169 ms 542284 KiB
test_0052.txt AC 863 ms 541004 KiB
test_0053.txt AC 1336 ms 542904 KiB
test_0054.txt AC 1008 ms 546300 KiB
test_0055.txt AC 991 ms 539740 KiB
test_0056.txt AC 1350 ms 545424 KiB
test_0057.txt AC 1294 ms 542364 KiB
test_0058.txt AC 1337 ms 539040 KiB
test_0059.txt AC 1273 ms 539840 KiB
test_0060.txt AC 980 ms 539928 KiB
test_0061.txt AC 1354 ms 542972 KiB
test_0062.txt AC 1396 ms 539732 KiB
test_0063.txt AC 1207 ms 504824 KiB
test_0064.txt AC 1079 ms 539592 KiB
test_0065.txt AC 1644 ms 539780 KiB
test_0066.txt AC 1000 ms 539472 KiB
test_0067.txt AC 1011 ms 543268 KiB
test_0068.txt AC 884 ms 517624 KiB
test_0069.txt AC 1383 ms 537280 KiB
test_0070.txt AC 1103 ms 539028 KiB
test_0071.txt AC 1669 ms 542284 KiB
test_0072.txt AC 1014 ms 539632 KiB
test_0073.txt AC 1348 ms 541064 KiB
test_0074.txt AC 940 ms 539096 KiB
test_0075.txt AC 933 ms 466892 KiB
test_0076.txt AC 1071 ms 539628 KiB
test_0077.txt AC 946 ms 542536 KiB
test_0078.txt AC 998 ms 520936 KiB
test_0079.txt AC 1313 ms 539916 KiB
test_0080.txt AC 1197 ms 542588 KiB
test_0081.txt AC 889 ms 540368 KiB
test_0082.txt AC 884 ms 542460 KiB
test_0083.txt AC 1070 ms 542456 KiB
test_0084.txt AC 1337 ms 539700 KiB
test_0085.txt AC 1346 ms 539864 KiB
test_0086.txt AC 1424 ms 539828 KiB
test_0087.txt AC 1324 ms 495836 KiB
test_0088.txt AC 1099 ms 514584 KiB
test_0089.txt AC 1103 ms 539800 KiB
test_0090.txt AC 1231 ms 539372 KiB
test_0091.txt AC 1182 ms 539952 KiB
test_0092.txt AC 1153 ms 540544 KiB
test_0093.txt AC 966 ms 542932 KiB
test_0094.txt AC 821 ms 539060 KiB
test_0095.txt AC 1290 ms 436952 KiB
test_0096.txt AC 1143 ms 545668 KiB
test_0097.txt AC 1111 ms 539816 KiB
test_0098.txt AC 952 ms 539596 KiB
test_0099.txt AC 1443 ms 540168 KiB
test_0100.txt AC 927 ms 539228 KiB
test_0101.txt AC 831 ms 540716 KiB
test_0102.txt AC 1235 ms 540504 KiB
test_0103.txt AC 1475 ms 517096 KiB
test_0104.txt AC 1351 ms 540292 KiB
test_0105.txt AC 1036 ms 539180 KiB
test_0106.txt AC 902 ms 540664 KiB
test_0107.txt AC 1473 ms 540180 KiB
test_0108.txt AC 852 ms 539608 KiB
test_0109.txt AC 1121 ms 540796 KiB
test_0110.txt AC 1681 ms 539920 KiB
test_0111.txt AC 1278 ms 542936 KiB
test_0112.txt AC 1092 ms 539584 KiB
test_0113.txt AC 1072 ms 492092 KiB
test_0114.txt AC 856 ms 543024 KiB
test_0115.txt AC 959 ms 539468 KiB
test_0116.txt AC 1367 ms 540088 KiB
test_0117.txt AC 1139 ms 529804 KiB
test_0118.txt AC 1988 ms 421168 KiB
test_0119.txt AC 1222 ms 542480 KiB
test_0120.txt AC 1151 ms 542848 KiB
test_0121.txt AC 1089 ms 542872 KiB
test_0122.txt AC 1261 ms 539676 KiB
test_0123.txt AC 1479 ms 539776 KiB
test_0124.txt AC 1238 ms 542152 KiB
test_0125.txt AC 1250 ms 542304 KiB
test_0126.txt AC 1177 ms 539760 KiB
test_0127.txt AC 1500 ms 520608 KiB
test_0128.txt AC 1251 ms 523328 KiB
test_0129.txt AC 1771 ms 542772 KiB
test_0130.txt AC 960 ms 539680 KiB
test_0131.txt AC 1384 ms 542964 KiB
test_0132.txt AC 1733 ms 539652 KiB
test_0133.txt AC 1061 ms 540080 KiB
test_0134.txt AC 1149 ms 539684 KiB
test_0135.txt AC 1529 ms 443416 KiB
test_0136.txt AC 1082 ms 542316 KiB
test_0137.txt AC 1021 ms 536892 KiB
test_0138.txt AC 1132 ms 540480 KiB
test_0139.txt AC 1450 ms 536612 KiB
test_0140.txt AC 1028 ms 539776 KiB
test_0141.txt AC 1366 ms 537848 KiB
test_0142.txt AC 890 ms 546104 KiB
test_0143.txt AC 1248 ms 542212 KiB
test_0144.txt AC 1051 ms 531044 KiB
test_0145.txt AC 1275 ms 539540 KiB
test_0146.txt AC 967 ms 540548 KiB
test_0147.txt AC 1417 ms 539688 KiB
test_0148.txt AC 1117 ms 537308 KiB
test_0149.txt AC 1266 ms 539964 KiB