from datetime import date # ====== 板面 ====== board = [ ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', ''], ['Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ''], ['1', '2', '3', '4', '5', '6', '7'], ['8', '9', '10', '11', '12', '13', '14'], ['15', '16', '17', '18', '19', '20', '21'], ['22', '23', '24', '25', '26', '27', '28'], ['29', '30', '31', 'Sun', 'Mon', 'Tue', 'Wed'], ['', '', '', '', 'Thu', 'Fri', 'Sat'] ] H, W = len(board), len(board[0]) # ====== 10 块拼图 ====== pieces = [ # 1: o o # o # o o [(0,0),(0,1),(1,0),(2,0),(2,1)], # 2: o o # o # o o [(0,0),(0,1),(1,1),(2,1),(2,2)], # 3: o # o # o o o [(0,1),(1,1),(2,0),(2,1),(2,2)], # 4: o # o o # o o [(0,0),(1,0),(1,1),(2,0),(2,1)], # 5: o o o o [(0,0),(0,1),(0,2),(0,3)], # 6: o o o # o # o [(0,0),(0,1),(0,2),(1,2),(2,2)], # 7: o o # o o o [(0,2),(0,3),(1,0),(1,1),(1,2)], # 8: o # o o o o [(0,3),(1,0),(1,1),(1,2),(1,3)], # 9: o # o # o o [(0,0),(1,0),(2,0),(2,1)], # 10: o # o o # o [(0,0),(1,0),(1,1),(2,1)], ] MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'] # Python weekday(): Mon=0..Sun=6 WDAYS = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'] def weekday_label(y,m,d): return WDAYS[date(y,m,d).weekday()] def find_label_pos(label): for r in range(H): for c in range(W): if board[r][c] == label: return (r,c) raise ValueError(f"板面找不到标签 {label}") def build_mask(y,m,d): """True=可铺;False=禁止(洞或需留空的3格)""" M = [[True]*W for _ in range(H)] # 洞不可铺 for r in range(H): for c in range(W): if board[r][c] == '': M[r][c] = False # 留空:月份、日期、星期 for lab in (MONTHS[m-1], str(d), weekday_label(y,m,d)): r,c = find_label_pos(lab) M[r][c] = False return M def normalize(shape): mr = min(r for r,c in shape); mc = min(c for r,c in shape) return tuple(sorted((r-mr, c-mc) for r,c in shape)) def rot90(shape): # (r,c) -> (c,-r) return normalize([(c, -r) for r,c in shape]) def flip_h(shape): # (r,c) -> (r,-c) return normalize([(r, -c) for r,c in shape]) def all_transforms(shape): seen = set() cur = normalize(shape) for _ in range(4): for s in (cur, flip_h(cur)): s = normalize(s) seen.add(s) cur = rot90(cur) return list(seen) def generate_placements(mask, piece): res = [] shapes = all_transforms(piece) for shp in shapes: maxr = max(r for r,c in shp); maxc = max(c for r,c in shp) for r0 in range(H - maxr): for c0 in range(W - maxc): cells = [] ok = True for dr,dc in shp: r,c = r0+dr, c0+dc if not mask[r][c]: ok = False; break cells.append((r,c)) if ok: res.append(tuple(cells)) # 去重 return list(dict.fromkeys(res)) def solve_one(y,m,d, find_all=False): """回溯 + MRV,默认返回一解。""" mask = build_mask(y,m,d) placeable = sum(mask[r][c] for r in range(H) for c in range(W)) total_cells = sum(len(p) for p in pieces) if placeable != total_cells: raise ValueError(f"面积不匹配:可铺位={placeable},pieces面积={total_cells}。") plist = [] for i,p in enumerate(pieces): opts = generate_placements(mask, p) if not opts: return None plist.append((i, opts)) plist.sort(key=lambda x: len(x[1])) # MRV used = [[False]*W for _ in range(H)] sol = [None]*len(pieces) solutions = [] def can_place(cells): return all(not used[r][c] for r,c in cells) def set_place(cells, val): for r,c in cells: used[r][c] = val def dfs(k): if k == len(plist): solutions.append(sol.copy()) return not find_all idx, opts = plist[k] for cells in opts: if can_place(cells): sol[idx] = cells set_place(cells, True) stop = dfs(k+1) set_place(cells, False) sol[idx] = None if stop: return True return False dfs(0) return solutions[0] if solutions else None def draw_ascii(solution, y,m,d): """A..J 表示 10 块;洞留空;月/日/星期保留原标签不覆盖。""" canvas = [[board[r][c] for c in range(W)] for r in range(H)] labels = [chr(ord('A')+i) for i in range(len(pieces))] for i,cells in enumerate(solution): for r,c in cells: canvas[r][c] = labels[i] def fmt(x): return str(x) if x != '' else ' ' return '\n'.join(' '.join(f"{fmt(canvas[r][c]):>3}" for c in range(W)) for r in range(H)) # ====== 示例:调用并打印字符画 ====== if __name__ == "__main__": Y, M, D = 2025, 8, 3 sol = solve_one(Y, M, D, find_all=False) if sol is None: print("未找到解") else: print(draw_ascii(sol, Y, M, D))