mirror of
https://github.com/InternLM/InternBootcamp.git
synced 2026-04-19 12:58:04 +00:00
140 lines
5.8 KiB
Python
Executable file
140 lines
5.8 KiB
Python
Executable file
def solve_campsite(grid, row_constraints, col_constraints):
|
||
"""
|
||
Solve the 'Campsite' puzzle using backtracking.
|
||
|
||
:param grid: A list of lists of characters ('T' or 'X') of size n×m.
|
||
:param row_constraints: A list of length n with the required tent count per row.
|
||
:param col_constraints: A list of length m with the required tent count per column.
|
||
:return: A list of lists representing a solution (replacing 'X' with either 'C' or 'X'),
|
||
or None if no solution exists.
|
||
"""
|
||
|
||
n = len(grid)
|
||
m = len(grid[0])
|
||
|
||
# Copy the grid to store the solution without mutating the original
|
||
solution = [row[:] for row in grid]
|
||
|
||
# Track how many tents are currently placed in each row/column
|
||
current_row_tents = [0] * n
|
||
current_col_tents = [0] * m
|
||
|
||
# Precompute which cells are adjacent to at least one tree
|
||
# because a tent can only go orthogonally adjacent to a tree
|
||
adjacent_to_tree = [[False]*m for _ in range(n)]
|
||
directions_orth = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
||
for r in range(n):
|
||
for c in range(m):
|
||
if grid[r][c] == 'T':
|
||
for dr, dc in directions_orth:
|
||
rr, cc = r+dr, c+dc
|
||
if 0 <= rr < n and 0 <= cc < m and grid[rr][cc] == 'X':
|
||
adjacent_to_tree[rr][cc] = True
|
||
|
||
# Helper function to check if placing a tent at (r, c) violates adjacency constraints
|
||
def can_place_tent(r, c):
|
||
# If the cell is not empty or not adjacent to a tree, cannot place a tent
|
||
if solution[r][c] != 'X' or not adjacent_to_tree[r][c]:
|
||
return False
|
||
|
||
# Check row/col constraints if adding one more tent is still valid
|
||
if current_row_tents[r] + 1 > row_constraints[r]:
|
||
return False
|
||
if current_col_tents[c] + 1 > col_constraints[c]:
|
||
return False
|
||
|
||
# Tents cannot be adjacent (orth or diag) to any existing tent
|
||
# We'll check the 8 directions around (r, c)
|
||
directions_all = [
|
||
(-1, 0), (1, 0), (0, -1), (0, 1),
|
||
(-1, -1), (-1, 1), (1, -1), (1, 1)
|
||
]
|
||
for dr, dc in directions_all:
|
||
rr, cc = r+dr, c+dc
|
||
if 0 <= rr < n and 0 <= cc < m and solution[rr][cc] == 'C':
|
||
return False
|
||
|
||
return True
|
||
|
||
# A list of all grid coordinates we might try to fill
|
||
# Sorting them can sometimes help performance, but plain is fine, too.
|
||
all_cells = [(r, c) for r in range(n) for c in range(m)]
|
||
history = []
|
||
|
||
# We'll do a simple backtracking approach that tries to place a tent or not
|
||
def backtrack(idx=0):
|
||
if idx == len(all_cells):
|
||
history.append(f"All positions have been tried. Check if the current solution satisfies the row constraints and the col constraints.\n")
|
||
is_satisfy = all(current_row_tents[r] == row_constraints[r] for r in range(n)) and all(current_col_tents[c] == col_constraints[c] for c in range(m))
|
||
if is_satisfy:
|
||
history.append("The current solution satisfy all the conditions. Find the solution!\n")
|
||
else:
|
||
history.append("The current solution does not satisfy all the conditions.\n")
|
||
return is_satisfy
|
||
|
||
r, c = all_cells[idx]
|
||
history.append(f"Select ({r},{c}), check if the tent can be placed in this location.\n")
|
||
|
||
# -----------------------------------------------------------
|
||
# OPTION 1: Place a tent in (r, c) if it's a valid move
|
||
# -----------------------------------------------------------
|
||
if can_place_tent(r, c):
|
||
history.append(f"Tent can be placed at ({r},{c}). Place a tent at ({r},{c}).\n")
|
||
solution[r][c] = 'C'
|
||
current_row_tents[r] += 1
|
||
current_col_tents[c] += 1
|
||
|
||
if backtrack(idx + 1):
|
||
return True
|
||
|
||
history.append(f"Since current solution fails. Change the current location ({r},{c}) from a tent(C) to an open space(X). Then retry.\n")
|
||
# Backtrack (undo placement)
|
||
solution[r][c] = 'X'
|
||
current_row_tents[r] -= 1
|
||
current_col_tents[c] -= 1
|
||
else:
|
||
history.append(f"No tents can be placed in ({r},{c}).\n")
|
||
|
||
# -----------------------------------------------------------
|
||
# OPTION 2: Skip placing a tent at (r, c)
|
||
# -----------------------------------------------------------
|
||
if backtrack(idx + 1):
|
||
return True
|
||
history.append(f"Since there is no tent placed at the current location ({r},{c}), we backtrack to the previous location.\n")
|
||
|
||
# If neither placing nor skipping yields a solution, fail here
|
||
return False
|
||
|
||
# Kick off the backtracking
|
||
if backtrack():
|
||
history.append('\nThe final answer is: '+'{'+'\n'.join([' '.join(row) for row in solution]) + '}')
|
||
steps = ''.join(history)
|
||
return solution, steps
|
||
else:
|
||
return None
|
||
|
||
# -----------------------------------------------------------------
|
||
# Example usage:
|
||
#
|
||
# Suppose you have the puzzle:
|
||
if __name__ == "__main__":
|
||
grid = [
|
||
['X','X','X','X','X','T','X','X','T','X'],
|
||
['X','T','X','X','X','X','X','X','X','X'],
|
||
['X','X','X','T','T','X','X','X','X','X'],
|
||
['T','X','X','X','X','X','T','T','X','X'],
|
||
['X','T','X','X','T','X','X','X','X','X']
|
||
]
|
||
row_constraints = [3, 1, 3, 0, 3]
|
||
col_constraints = [2, 1, 1, 1, 1, 0, 2, 1, 0, 1]
|
||
|
||
solved, steps = solve_campsite(grid, row_constraints, col_constraints)
|
||
if solved:
|
||
# Format the output as requested
|
||
print(steps)
|
||
else:
|
||
print("No solution found (but puzzle is assumed to have a unique solution).")
|
||
#
|
||
# You can adapt this to parse your input format, then call `solve_campsite`
|
||
# to get the solution, and finally output the result in the requested
|
||
# "[[row1, row2, ...]]" string format.
|