import csv from collections import deque class TreeNode: def __init__(self): self.fa = None self.children = [] self.pos = None self.final_pos = None self.val = 0 self.id = 0 self.dp = 0 class SourceCollector: def __init__(self, filename=None, maze=None): self.filename = filename self.maze = maze self.start_pos = None self.end_pos = None self.path = [] self.node_path = [] self.boss_pos = None self.lock_pos = None if self.filename: self.maze = [] with open(f"{self.filename}",'r') as f: reader = csv.reader(f) for idx,row in enumerate(reader): t = [] for idy,i in enumerate(row): if i.startswith('b'): t.append('0') self.boss_pos = (idx,idy) elif i.startswith('l'): t.append('0') self.lock_pos = (idx,idy) else: t.append(i) self.maze.append(t) else: self.maze = maze self.rowNums = len(self.maze) self.colNums = len(self.maze[0]) for i in range(self.rowNums): for j in range(self.colNums): if self.maze[i][j] =='s': self.start_pos = (i,j) if self.maze[i][j] =='e': self.end_pos = (i,j) def dfs_show(self,u): if u.id != 0: print(f"id: {u.id} , fa:{u.fa.id} , val:{u.val} , pos:{u.pos}") else: print(f"id: {u.id} , val:{u.val} , pos:{u.pos}") for child in u.children: self.dfs_show(child) def build_a_tree(self): cnt = 0 root = TreeNode() root.pos = self.start_pos root.id = 0 root.val = 0 root.fa = None queue = deque([(self.start_pos[0], self.start_pos[1], root)]) st = [[False] * self.colNums for _ in range(self.rowNums)] st[self.start_pos[0]][self.start_pos[1]] = True dx = [-1, 0, 1, 0] dy = [0, -1, 0, 1] while queue: x, y, parent = queue.popleft() for i in range(4): nx, ny = x + dx[i], y + dy[i] if self.outofmap(nx, ny) or st[nx][ny]: continue if self.maze[nx][ny] != '1': st[nx][ny] = True new_node = TreeNode() new_node.pos = (nx, ny) new_node.fa = parent cnt+=1 new_node.id = cnt if self.maze[nx][ny].startswith('g'): new_node.val = int(self.maze[nx][ny][1:]) elif self.maze[nx][ny].startswith('t'): new_node.val =-1 *int(self.maze[nx][ny][1:]) parent.children.append(new_node) queue.append((nx, ny, new_node)) return root def outofmap(self,x,y): return x < 0 or y < 0 or x >= self.rowNums or y >= self.colNums def getlca(self,u, v): def get_path_to_root(node): path = [] while node: path.append(node) node = node.fa return path path_u = get_path_to_root(u) path_v = get_path_to_root(v) path_u.reverse() path_v.reverse() lca = None for i in range(min(len(path_u),len(path_v))): if path_u[i] == path_v[i]: lca = path_u[i] else: break if lca is None: return [] u_to_lca = [] node = u while node != lca: u_to_lca.append(node) node = node.fa lca_to_v = [] node_list = [] node = v while node != lca: node_list.append(node) node = node.fa node_list.append(lca) node_list.reverse() for node in node_list: lca_to_v.append(node) full_path = u_to_lca + lca_to_v[:-1] return full_path def dfs(self, sn): sn.dp = sn.val sn.final_pos = sn.pos sn.path = [sn] # 先对子节点全部深搜一遍,收集路径长度 children = sn.children[:] for child in children: self.dfs(child) if self.colNums < 11: children.sort(key=lambda c: len(c.path)) cur = None for idx, child in enumerate(children): if child.dp > 0: sn.dp += child.dp if cur is not None: sn.path.extend(self.getlca(sn.path[-1], child)) sn.path.extend(child.path) cur = child sn.final_pos = cur.final_pos def get_path(self): return self.path def bfs_path(self, start, end): """从start到end的最短路径(含首尾)""" from collections import deque n, m = self.rowNums, self.colNums visited = [[False]*m for _ in range(n)] prev = [[None]*m for _ in range(n)] q = deque([start]) visited[start[0]][start[1]] = True dx = [-1, 0, 1, 0] dy = [0, -1, 0, 1] while q: x, y = q.popleft() if (x, y) == end: break for i in range(4): nx, ny = x + dx[i], y + dy[i] if 0 <= nx < n and 0 <= ny < m and not visited[nx][ny]: if self.maze[nx][ny] != '1': visited[nx][ny] = True prev[nx][ny] = (x, y) q.append((nx, ny)) # 回溯路径 path = [] cur = end while cur and cur != start: path.append(cur) cur = prev[cur[0]][cur[1]] if cur == start: path.append(start) path.reverse() return path return [] def run(self): sn = self.build_a_tree() # self.dfs_show(sn) self.dfs(sn) self.path =[_.pos for _ in sn.path] for idx,item in enumerate(self.path): if idx > 0: if item == self.path[idx-1]: del self.path[idx] self.path.extend(self.bfs_path(self.path[-1],self.end_pos)) def output_list(self): copy_maze = self.maze for idx, (y, x) in enumerate(self.path): if copy_maze[y][x].startswith('s') | copy_maze[y][x].startswith('e'): continue if copy_maze[y][x].startswith('g') | copy_maze[y][x].startswith('t'): copy_maze[y][x] = f"{copy_maze[y][x]}p{idx}" continue copy_maze[y][x] = f"p{idx}" return copy_maze if __name__ == '__main__': obj = SourceCollector(filename="maze.csv") obj.run() path = obj.get_path() for i in path: print(i) # print(sn.pos) # pre = sn.pos # for _ in sn.dp_path: # dx,dy = _[0] - pre[0],_[1]-pre[1] # if dx > 0: # print("down") # elif dx < 0: # print("up") # elif dy > 0: # print("right") # elif dy < 0: # print("left") # pre = _