結果

問題 No.86 TVザッピング(2)
ユーザー qwewe
提出日時 2025-05-14 12:58:33
言語 PyPy3
(7.3.15)
結果
WA  
実行時間 -
コード長 9,594 bytes
コンパイル時間 189 ms
コンパイル使用メモリ 81,968 KB
実行使用メモリ 151,920 KB
最終ジャッジ日時 2025-06-20 14:02:29
合計ジャッジ時間 8,118 ms
ジャッジサーバーID
(参考情報)
judge2 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
sample AC * 2 WA * 1
other AC * 6 WA * 1 TLE * 1 -- * 22
権限があれば一括ダウンロードができます

ソースコード

diff #

import sys

# Increase recursion depth limit for potentially deep paths.
# The maximum path length (number of moves) is T, which can be up to N*M = 100*100 = 10000.
# The recursion depth can reach T. Setting it slightly above the maximum possible T.
try:
    sys.setrecursionlimit(10500) 
except OverflowError: # Handle potential OS limits on stack size
    # If setting a high limit fails, use a default reasonable limit or warn.
    # This might cause RecursionError on large test cases if the limit is too low.
    # Let's just let it fail if the system cannot handle it. The competitive programming environment usually allows high limits.
     pass

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

    # Find all available cells (marked with '.')
    available_cells = []
    for r in range(N):
        for c in range(M):
            if A[r][c] == '.':
                available_cells.append((r, c))

    # Total number of available cells
    T = len(available_cells)

    # Based on graph theory (Hamiltonian cycle on bipartite graph) and problem rules analysis:
    # 1. The number of available cells T must be even.
    # 2. Analysis showed T=2 is impossible due to turn constraints.
    # Therefore, T must be even and T >= 4 for a cycle to possibly exist.
    if T % 2 != 0 or T < 4:
        print("NO")
        return
    
    # Precompute adjacency list for available cells for efficiency.
    # adj maps cell coordinates (tuple) to a list of adjacent available cell coordinates.
    adj = {}
    for r_idx in range(N):
        for c_idx in range(M):
            if A[r_idx][c_idx] == '.':
                curr_cell = (r_idx, c_idx)
                adj[curr_cell] = []
                # Check potential neighbors in 4 cardinal directions
                for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]: # Right, Left, Down, Up
                    nr, nc = r_idx + dr, c_idx + dc
                    # Check if neighbor is within grid boundaries and is an available cell '.'
                    if 0 <= nr < N and 0 <= nc < M and A[nr][nc] == '.':
                        adj[curr_cell].append((nr, nc))

    # Keep track of visited cells for the current DFS path. Use a 2D list for potentially better performance than dict.
    visited = [[False for _ in range(M)] for _ in range(N)]

    # Global variables to store context about the start of the path, needed for cycle closure checks.
    start_cell_global = None # Stores the coordinates (tuple) of the starting cell for the current DFS attempt
    first_move_dir_global = None # Stores the direction (dr, dc tuple) of the first move (v0 -> v1)

    # Helper function to calculate the direction vector (dr, dc) from one cell to an adjacent cell.
    def get_dir(from_cell, to_cell):
        return (to_cell[0] - from_cell[0], to_cell[1] - from_cell[1])

    # Helper function to calculate the direction resulting from a 90-degree left turn.
    def left_turn(dr, dc):
        # Rotation logic: (dr, dc) rotated left 90 degrees becomes (-dc, dr).
        return (-dc, dr)

    # Helper function to check if `curr_dir` is a valid next direction after `prev_dir`
    # according to the problem rule: "straight or left turn".
    def check_turn(prev_dir, curr_dir):
        # Check if move is straight
        if curr_dir == prev_dir:
            return True
        # Check if move is a left turn
        lt_dir = left_turn(prev_dir[0], prev_dir[1])
        if curr_dir == lt_dir:
            return True
        # If neither, the move direction is invalid by the rules
        return False

    # Depth First Search function to explore paths and check for a valid cycle.
    # Parameters:
    #   curr_cell: The current cell coordinates (tuple).
    #   prev_cell: The coordinates of the cell visited just before curr_cell (tuple).
    #   visited_count: The number of distinct available cells visited so far in the current path.
    def dfs(curr_cell, prev_cell, visited_count):
        
        curr_r, curr_c = curr_cell
        
        # Determine the direction of the move that arrived at curr_cell.
        curr_dir = get_dir(prev_cell, curr_cell)
        
        # Base Case: If T cells have been visited, check if path can close into a valid cycle.
        if visited_count == T:
            # Need to check if moving from curr_cell back to start_cell_global is possible and valid.
            
            # 1. Adjacency Check: Is start_cell_global adjacent to curr_cell?
            # Use Manhattan distance for grid adjacency check.
            if abs(curr_r - start_cell_global[0]) + abs(curr_c - start_cell_global[1]) != 1:
                 return False # Not adjacent, cannot form cycle.

            # 2. Calculate Final Move Direction: Determine direction for the move back to start.
            final_dir = get_dir(curr_cell, start_cell_global)

            # 3. Final Move Turn Rule Check: Does the final move (v_{T-1} -> v_0) follow the turn rule based on the previous move (v_{T-2} -> v_{T-1})?
            # The direction of the move v_{T-2} -> v_{T-1} is stored in curr_dir.
            if not check_turn(curr_dir, final_dir):
                return False

            # 4. First Move Turn Rule Check (Cycle Consistency): Does the first move (v_0 -> v_1) follow the turn rule based on the final move (v_{T-1} -> v_0)?
            # This ensures the turn rule holds across the cycle wrap-around.
            if not check_turn(final_dir, first_move_dir_global):
                return False
            
            # If all checks pass, we have found a valid cycle satisfying all conditions.
            return True

        # Recursive Step: Explore possible next moves from curr_cell.

        # Mark current cell as visited for this path exploration.
        visited[curr_r][curr_c] = True
        
        # Calculate the two possible next directions according to the rules: straight or left turn.
        straight_dir = curr_dir
        left_turn_dir = left_turn(curr_dir[0], curr_dir[1])
        
        # Try moving Straight
        next_cell_S = (curr_r + straight_dir[0], curr_c + straight_dir[1])
        # Check if the cell is within bounds and is an available cell ('.')
        if 0 <= next_cell_S[0] < N and 0 <= next_cell_S[1] < M and A[next_cell_S[0]][next_cell_S[1]] == '.':
             # Check if the cell hasn't been visited yet in the current path
             if not visited[next_cell_S[0]][next_cell_S[1]]:
                 # Recursively call DFS for the next cell
                 if dfs(next_cell_S, curr_cell, visited_count + 1):
                     # If recursive call found a solution, propagate True up the call stack.
                     return True 
             # If the target is the start cell, it must be the final move to close the cycle.
             # This logic is implicitly handled by the base case check when visited_count reaches T.
             # The `not visited` check correctly prevents revisiting start_cell_global prematurely.

        # Try moving Left Turn
        next_cell_L = (curr_r + left_turn_dir[0], curr_c + left_turn_dir[1])
        # Check if the cell is valid
        if 0 <= next_cell_L[0] < N and 0 <= next_cell_L[1] < M and A[next_cell_L[0]][next_cell_L[1]] == '.':
             # Check if the cell hasn't been visited yet
             if not visited[next_cell_L[0]][next_cell_L[1]]:
                 # Recurse
                 if dfs(next_cell_L, curr_cell, visited_count + 1):
                     return True
             # Similar implicit handling if next_cell_L is the start cell.

        # Backtrack: If no path found from curr_cell, unmark it as visited.
        # This allows other paths to potentially use this cell.
        visited[curr_r][curr_c] = False
        
        # Return False indicating no solution found from this state downwards.
        return False

    # Main loop to initiate DFS search attempts.
    # Try starting the path from each available cell.
    for r_start, c_start in available_cells:
        start_cell = (r_start, c_start)
        
        # A starting cell must have at least one neighbor to start a path.
        # adj check handles isolated '.' cells, although problem guarantees T>=2.
        if start_cell not in adj or not adj[start_cell]: continue 

        # Try each neighbor of the start cell as the target of the first move.
        for first_neighbor in adj[start_cell]:
            
            # Reset the visited state tracker for each new DFS attempt.
            for r_vis in range(N):
                for c_vis in range(M):
                    visited[r_vis][c_vis] = False
            
            # Set the global context variables for this specific DFS run.
            start_cell_global = start_cell
            first_move_dir_global = get_dir(start_cell, first_neighbor)
            
            # Mark the starting cell as visited before initiating DFS.
            visited[r_start][c_start] = True 
            
            # Start the DFS from the first neighbor. 
            # The initial path has length 1 (1 move), involves 2 cells (start and first neighbor).
            # So, visited_count starts at 2.
            if dfs(first_neighbor, start_cell, 2):
                # If DFS returns True, a valid cycle path has been found.
                print("YES")
                return # Exit the function immediately.

    # If the loops complete without finding any valid path from any start cell / first move combination.
    print("NO")

# Call the main solve function to run the logic.
solve()
0