1810. 隐藏网格下的最小消耗路径
这是一个交互问题。
有一个机器人存在于网格中,你需要通过不断尝试使他从初始单元到达目标单元。网格的规格为m x n,并且每个单元的属性值要不为空,要不已被占用。题目保证初始网格和目标网格不同且均为空。
每个单元格都有消耗值,你需要在每次移动至此单元格后支付该费用。在机器人启动前,初始单元的费用不被计算在内。
你需要找到机器人移动至目标网格的最小总消耗。但可惜的是你并不知道网格的尺寸、初始单元和目标单元。你只允许通过询问GridMaster
类获得信息。
GridMaster
类存在以下功能:
boolean canMove(char direction)
当机器人可以向这个方向移动时,返回true
;反之返回false
。int move(char direction)
沿该方向移动机器人,并返回移动到该单元的消耗值。如果此移动将机器人移动到被占有的单元格或离开网格,则移动将被忽略,机器人将保持在相同的位置,函数将返回-1
。boolean isTarget()
:如果机器人当前位于目标单元格上,则返回true
;反之返回 false
。请注意,上述函数中的方向应该是{ 'U'、'D'、'L'、'R' }
中的字符,分别表示向上、向下、左和右方向。
返回使机器人从其初始起始单元到目标单元的最小总消耗。如果单元格之间不存在有效路径,则返回-1
。
测试实例:
测试输入一个大小为m x n
的二维数组 grid
和四个int
型参数 r1
, c1
, r2
, 和 c2
:
grid[i][j] == 0
表示网格 (i, j)
已被占用。grid[i][j] >= 1
表示网格单元 (i, j)
为空并且 grid[i][j]
的值为移动至此网格的成本值。(r1, c1)
为初始单元。(r2, c2)
为目标单元。请注意,你将无法在你的代码中获知这些信息。
示例 1:
输入: grid = [[2,3],[1,1]], r1 = 0, c1 = 1, r2 = 1, c2 = 0 输出: 2 解释: 其中一种可能路径描述如下: 机器人最开始站在单元格 (0, 1) ,用 3 表示 - master.canMove('U') 返回 false - master.canMove('D') 返回 true - master.canMove('L') 返回 true - master.canMove('R') 返回 false - master.move('L') 机器人移动到单元格 (0, 0) 并返回 2 - master.isTarget() 返回 false - master.canMove('U') 返回 false - master.canMove('D') 返回 true - master.canMove('L') 返回 false - master.canMove('R') 返回 true - master.move('D') 机器人移动到单元格 (1, 0) 并返回 1 - master.isTarget() 返回 true - master.move('L') 机器人不移动并返回 -1 - master.move('R') 机器人移动到单元格 (1, 1) 并返回 1 现在我们知道了机器人达到目标单元(1, 0)的最小消耗成本为2。
示例 2:
输入: grid = [[0,3,1],[3,4,2],[1,2,0]], r1 = 2, c1 = 0, r2 = 0, c2 = 2 输出: 9 解释: 最小消耗路径为 (2,0) -> (2,1) -> (1,1) -> (1,2) -> (0,2).
示例 3:
输入: grid = [[1,0],[0,1]], r1 = 0, c1 = 0, r2 = 1, c2 = 1 输出: -1 解释: 不存在可使机器人到达目标单元的路径。
提示:
1 <= n, m <= 100
m == grid.length
n == grid[i].length
0 <= grid[i][j] <= 100
原站题解
python3 解法, 执行用时: 864 ms, 内存消耗: 32.7 MB, 提交时间: 2023-10-22 10:03:05
# """ # This is GridMaster's API interface. # You should not implement it, or speculate about its implementation # """ #class GridMaster(object): # def canMove(self, direction: str) -> bool: # # # def move(self, direction: str) -> int: # # # def isTarget(self) -> None: # # class Solution(object): def findShortestPath(self, master: 'GridMaster') -> int: def dfs(i, j): nonlocal grid, directions, visited, findtarget, endx, endy visited.add((i, j)) # 加入已经访问 if master.isTarget(): # 目标位置 endx, endy = i, j findtarget = True for dx, dy, d, revd in directions: if (i+dx, j+dy) not in visited and master.canMove(d): grid[i+dx][j+dy] = master.move(d) # 记录成本 dfs(i+dx, j+dy) master.move(revd) # 回溯 return grid = [[0 for _ in range(201)] for _ in range(201)] directions = [(0,1,"U","D"), (0,-1,"D","U"), (1,0,"L","R"), (-1,0,"R","L")] visited = set() findtarget = False endx, endy = -1, -1 # 从初始位置 dfs搜索整个图 # 假设初始位置是在100,100 可以有效防止下标越界 dfs(100,100) if not findtarget: # 没能找到target直接返回-1 return -1 # Dijkstra找到最短距离 heap = [(0,100,100)] # 初始位置 visited = set() while len(heap): cost, curx, cury = heapq.heappop(heap) if (curx, cury) == (endx, endy): # 找到了 return cost if (curx, cury) in visited: # 避免重复访问 continue visited.add((curx, cury)) # 标记已经访问 for dx, dy, _, _ in directions: nxtx, nxty = curx+dx, cury+dy # 节点没有越界 可以访问且也没有被访问过 if (0<=nxtx<=200 and 0 <=nxty<=200 and (nxtx, nxty) not in visited and grid[nxtx][nxty]): heapq.heappush(heap, (cost+grid[nxtx][nxty], nxtx, nxty)) # 加入最小堆 return -1
python3 解法, 执行用时: 940 ms, 内存消耗: 37 MB, 提交时间: 2023-10-22 10:02:36
# """ # This is GridMaster's API interface. # You should not implement it, or speculate about its implementation # """ #class GridMaster(object): # def canMove(self, direction: str) -> bool: # # # def move(self, direction: str) -> int: # # # def isTarget(self) -> None: # # from queue import PriorityQueue class Solution(object): def findShortestPath(self, master: 'GridMaster') -> int: vals = {} vals[(0, 0)] = 0 t_i, t_j = None, None def dfs(ii, jj): nonlocal t_i, t_j if master.isTarget(): t_i, t_j = ii, jj if (ii-1, jj) not in vals and master.canMove('U'): vals[(ii-1, jj)] = master.move('U') dfs(ii-1, jj) master.move('D') if (ii+1, jj) not in vals and master.canMove('D'): vals[(ii+1, jj)] = master.move('D') dfs(ii+1, jj) master.move('U') if (ii, jj-1) not in vals and master.canMove('L'): vals[(ii, jj-1)] = master.move('L') dfs(ii, jj-1) master.move('R') if (ii, jj+1) not in vals and master.canMove('R'): vals[(ii, jj+1)] = master.move('R') dfs(ii, jj+1) master.move('L') dfs(0, 0) if t_i is None: return -1; que = PriorityQueue() que.put((0, 0, 0)) best = {} best[(0, 0)] = 0 while que.qsize(): cost, i, j = que.get() if (i,j) == (t_i, t_j): return cost for ii, jj in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]: if (ii, jj) in vals and ((ii, jj) not in best or cost + vals[(ii, jj)] < best[(ii, jj)]): best[(ii, jj)] = cost + vals[(ii, jj)] que.put((cost + vals[(ii, jj)], ii, jj)) return -1
cpp 解法, 执行用时: 68 ms, 内存消耗: 17.6 MB, 提交时间: 2023-10-22 10:01:52
/** * // This is the GridMaster's API interface. * // You should not implement it, or speculate about its implementation * class GridMaster { * public: * bool canMove(char direction); * int move(char direction); * boolean isTarget(); * }; */ class Solution { public: char chs[4]={'U','D','L','R'}; char antiChs[4]={'D','U','R','L'}; int d[4][2]={{-1,0},{1,0},{0,-1},{0,1}}; bool find = false; int targetX,targetY; int cost[200][200]; void dfs(GridMaster& master,int x,int y){ //判断是否为目标 if(master.isTarget()){ targetX = x; targetY = y; find = true; } for(int i =0;i<4;i++){ //四个方向尝试能够走通 if(master.canMove(chs[i])){ //这个地方走过 if(cost[x+d[i][0]][y+d[i][1]]>=0){ continue; } //标记这个节点的代价 cost[x+d[i][0]][y+d[i][1]] = master.move(chs[i]); dfs(master,x+d[i][0],y+d[i][1]); master.move(antiChs[i]); } } } int findShortestPath(GridMaster &master) { //初始化所有结点的代价为负 memset(cost,0XFF,sizeof(cost)); //起点的代价为0 cost[100][100] = 0; dfs(master,100,100); //不能到达最后一个结点 if(!find){ return -1; } priority_queue<pair<int,pair<int,int>>,vector<pair<int,pair<int,int>>>,greater<pair<int,pair<int,int>>>> q; q.push({0,{100,100}}); int vis[200][200]; memset(vis,0x3f,sizeof(vis)); vis[100][100] = 0; int ans = 0; while(!q.empty()){ auto item = q.top(); q.pop(); int curr_cost = item.first; int x = item.second.first; int y = item.second.second; //这个节点之前访问过 if(vis[x][y]<curr_cost){ continue; } //找到目标 if(x==targetX&&y==targetY){ ans = curr_cost; break; } for(int i =0;i<4;i++){ int _x = x + d[i][0]; int _y = y + d[i][1]; //越界检查 if(_x<0||_x>=200||_y<0||_y>=200){ continue; } //这个点不能访问 或者这个点通过其他路径的代价更低(或相同) if(cost[_x][_y]<0||vis[_x][_y]<=curr_cost+cost[_x][_y]){ continue; } //更新当前结点的最小访问代价 vis[_x][_y] = curr_cost+cost[_x][_y]; q.push({curr_cost+cost[_x][_y],{_x,_y}}); } } return ans; } };
cpp 解法, 执行用时: 64 ms, 内存消耗: 17.6 MB, 提交时间: 2023-10-22 10:01:24
/** * // This is the GridMaster's API interface. * // You should not implement it, or speculate about its implementation * class GridMaster { * public: * bool canMove(char direction); * int move(char direction); * boolean isTarget(); * }; */ class Solution { private: // 已知大小是100,那么数组最大就是200的范围, 表示每一格移动的成本 int cost[200][200]; // 四个方向 char charDirs[4] = {'D', 'R', 'U', 'L'}; // 反方向,回溯时候使用 char charAntiDirs[4] = {'U', 'L', 'D', 'R'}; // 对应x,y移动的变量,直接保存在一个数组里 int intDirs[5] = {0, 1, 0, -1, 0}; // 目标对应的坐标 int tx; int ty; struct Node { int distance; int x; int y; Node(int distance, int x, int y): distance(distance), x(x),y(y) {} // 小顶堆,距离越小在堆顶 bool operator< (const Node &b) const { return distance > b.distance; } }; // 构建图的dfs void dfs(GridMaster& master, int x, int y) { // 判断是否是目标 if (master.isTarget()) { tx = x; ty = y; } // 四个方向去尝试 for (int i = 0; i < 4; ++i) { if (master.canMove(charDirs[i])) { // 只考虑没有遍历过的 int nx = x+intDirs[i]; int ny = y+intDirs[i+1]; if (cost[nx][ny] < 0) { cost[nx][ny] = master.move(charDirs[i]); // cout << nx << "," << ny << " " << cost[nx][ny] << endl; dfs(master, nx, ny); // 回溯回去尝试下一种方向 master.move(charAntiDirs[i]); } } } } public: int findShortestPath(GridMaster &master) { // 默认目标为负值无效 tx = -1; ty = -1; // 设置cost为-1作为默认值 memset(cost, 0xff, sizeof(int)*200*200); // 起点就是200的中点 100,100 cost[100][100] = 0; dfs(master, 100, 100); // 判断是否能到终点 if (tx == -1 || ty == -1) { return -1; } // 记录已经遍历的距离 int distances[200][200]; memset(distances, 0x3f3f3f3f, sizeof(distances)); priority_queue<Node> q; // 起点距离为0 distances[100][100] = 0; q.emplace(0, 100, 100); while (!q.empty()) { auto& curr = q.top(); int dist = curr.distance; int x = curr.x; int y = curr.y; q.pop(); // 忽略距离更大的情况 if (dist <= distances[x][y]) { if (x == tx && y == ty) { // 找到终点直接返回 return dist; } // 四个方向去遍历 for (int i = 0; i < 4; ++i) { int nx = x + intDirs[i]; int ny = y + intDirs[i+1]; // 保证可达,且距离更小 if (nx >= 0 && nx < 200 && ny >= 0 && ny < 200 && cost[nx][ny] >= 0) { if (distances[nx][ny] > dist + cost[nx][ny]) { distances[nx][ny] = dist + cost[nx][ny]; q.emplace(distances[nx][ny] , nx, ny); } } } } } return 0; } };