灵神力扣题单之网格图(下)

21 阅读10分钟

1 介绍

本博客用来记录灵神力扣题单之网格图

2 训练

2.5 综合应用

题目50LCP 45. 自行车炫技赛场

解题思路:三维状态bfs。

C++代码如下,

class Solution {
public:
    vector<vector<int>> bicycleYard(vector<int>& position, vector<vector<int>>& terrain, vector<vector<int>>& obstacle) {
        int n = terrain.size();
        int m = terrain[0].size();
        int dirs[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
        vector<vector<int>> res;
        set<vector<int>> visited;
        queue<vector<int>> q;
        q.push({position[0],position[1],1});
        visited.insert({position[0],position[1],1});
        while (!q.empty()) {
            auto t = q.front();
            int i = t[0], j = t[1], v = t[2];
            q.pop();
            if (v == 1 && !(i == position[0] && j == position[1])) {
                res.push_back({i,j});
            }
            for (int k = 0; k < 4; ++k) {
                int ni = i + dirs[k][0];
                int nj = j + dirs[k][1];
                if (ni < 0 || ni >= n || nj < 0 || nj >= m) continue;
                int nv = v + terrain[i][j] - terrain[ni][nj] - obstacle[ni][nj];
                if (nv < 1) continue;
                if (visited.find({ni,nj,nv}) != visited.end()) continue;
                q.push({ni,nj,nv});
                visited.insert({ni,nj,nv});
            }
        }
        sort(res.begin(), res.end());
        return res;
    }
};

python3代码如下,

class Solution:
    def bicycleYard(self, position: List[int], terrain: List[List[int]], obstacle: List[List[int]]) -> List[List[int]]:
        n = len(terrain)
        m = len(terrain[0])
        dirs = [[-1,0],[0,-1],[1,0],[0,1]] 
        res = []
        
        visited = set()
        q = collections.deque([])
        q.append((position[0],position[1],1))
        while len(q) > 0:
            i,j,v = q.popleft()
            if v == 1 and not (i == position[0] and j == position[1]):
                res.append([i,j])
            for k in range(4):
                ni = i + dirs[k][0]
                nj = j + dirs[k][1]
                if ni < 0 or ni >= n or nj < 0 or nj >= m:
                    continue 
                nv = v + terrain[i][j] - terrain[ni][nj] - obstacle[ni][nj]
                if nv < 1:
                    continue 
                if (ni,nj,nv) in visited:
                    continue 
                q.append((ni,nj,nv))
                visited.add((ni,nj,nv))
        res.sort()
        return res 

题目51LCP 75. 传送卷轴

解题思路:bfs,然后dfs,特别要注意dfs的实现。

C++代码如下,

class Solution {
public:
    int challengeOfTheKeeper(vector<string>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        int sx = -1, sy = -1;
        int ex = -1, ey = -1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == 'S') {
                    sx = i;
                    sy = j;
                } else if (grid[i][j] == 'T') {
                    ex = i;
                    ey = j;
                }
            }
        }
        int dirs[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};

        //计算终点到网格(i,j)的最短距离
        const int inf = 1e9;
        vector<vector<int>> d(n, vector<int>(m, inf));
        queue<pair<int,int>> q;
        d[ex][ey] = 0;
        q.emplace(ex,ey);
        while (!q.empty()) {
            auto [i,j] = q.front();
            q.pop();
            for (int k = 0; k < 4; ++k) {
                int ni = i + dirs[k][0];
                int nj = j + dirs[k][1];
                if (ni < 0 || ni >= n || nj < 0 || nj >= m) continue;
                if (grid[ni][nj] == '#') continue;
                if (d[ni][nj] != inf) continue;
                d[ni][nj] = d[i][j] + 1;
                q.emplace(ni,nj);
            }
        }

        vector<vector<int>> vis(n, vector<int>(m, -1));
        //判断在maxDis下,能不能走到终点
        auto check =[&] (int maxDis) -> bool {
            auto dfs =[&] (auto&& dfs, int i, int j) -> bool { //加上auto&& dfs就不会超时了
                if (i < 0 || i >= n || j < 0 || j >= m || grid[i][j] == '#' || vis[i][j] == maxDis) {
                    return false;
                }
                if (grid[i][j] == 'T') {
                    return true;
                }
                vis[i][j] = maxDis;
                if (grid[i][j] == '.' && ((grid[n-1-i][j] != '#' && d[n-1-i][j] > maxDis) || (grid[i][m-1-j] != '#' && d[i][m-1-j] > maxDis))) {
                    return false;
                }
                for (int k = 0; k < 4; ++k) {
                    int ni = i + dirs[k][0];
                    int nj = j + dirs[k][1];
                    if (dfs(dfs,ni,nj)) {
                        return true;
                    }
                }
                return false;
            };
            return dfs(dfs,sx,sy);
        };

        int res = -1;
        int left = 0;
        int right = n * m + 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (check(mid)) {
                res = mid;
                right = mid - 1; //求最小值
            } else {
                left = mid + 1;
            }
        }
        return res;
    }
};
//超出时间限制
class Solution {
public:
    int challengeOfTheKeeper(vector<string>& grid) {
        int n = grid.size();
        int m = grid[0].size();
        int sx = -1, sy = -1;
        int ex = -1, ey = -1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == 'S') {
                    sx = i;
                    sy = j;
                } else if (grid[i][j] == 'T') {
                    ex = i;
                    ey = j;
                }
            }
        }
        int dirs[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};

        //计算终点到网格(i,j)的最短距离
        const int inf = 1e9;
        vector<vector<int>> d(n, vector<int>(m, inf));
        queue<pair<int,int>> q;
        d[ex][ey] = 0;
        q.emplace(ex,ey);
        while (!q.empty()) {
            auto [i,j] = q.front();
            q.pop();
            for (int k = 0; k < 4; ++k) {
                int ni = i + dirs[k][0];
                int nj = j + dirs[k][1];
                if (ni < 0 || ni >= n || nj < 0 || nj >= m) continue;
                if (grid[ni][nj] == '#') continue;
                if (d[ni][nj] != inf) continue;
                d[ni][nj] = d[i][j] + 1;
                q.emplace(ni,nj);
            }
        }

        vector<vector<int>> vis(n, vector<int>(m, -1));
        //判断在maxDis下,能不能走到终点
        function<bool(int)> check =[&] (int maxDis) -> bool {
            function<bool(int,int)> dfs =[&] (int i, int j) -> bool {
                if (i < 0 || i >= n || j < 0 || j >= m || grid[i][j] == '#' || vis[i][j] == maxDis) {
                    return false;
                }
                if (grid[i][j] == 'T') {
                    return true;
                }
                vis[i][j] = maxDis;
                if (grid[i][j] == '.' && ((grid[n-1-i][j] != '#' && d[n-1-i][j] > maxDis) || (grid[i][m-1-j] != '#' && d[i][m-1-j] > maxDis))) {
                    return false;
                }
                for (int k = 0; k < 4; ++k) {
                    int ni = i + dirs[k][0];
                    int nj = j + dirs[k][1];
                    if (dfs(ni,nj)) {
                        return true;
                    }
                }
                return false;
            };
            return dfs(sx,sy);
        };

        int res = -1;
        int left = 0;
        int right = n * m + 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (check(mid)) {
                res = mid;
                right = mid - 1; //求最小值
            } else {
                left = mid + 1;
            }
        }
        return res;
    }
};

python3代码如下,

class Solution:
    def challengeOfTheKeeper(self, grid: List[str]) -> int:
        n = len(grid)
        m = len(grid[0])
        dirs = [[-1,0],[0,-1],[1,0],[0,1]]
        sx, sy = -1, -1
        ex, ey = -1, -1
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 'S':
                    sx, sy = i, j
                elif grid[i][j] == 'T':
                    ex, ey = i, j
        
        #从终点到(i,j)的最小距离
        #INF = int(1e9)
        d1 = [[inf] * m for _ in range(n)] 
        d1[ex][ey] = 0
        q = collections.deque([])
        q.append([ex,ey])
        while len(q) > 0:
            i, j = q.popleft()
            for k in range(4):
                ni = i + dirs[k][0]
                nj = j + dirs[k][1]
                if ni < 0 or ni >= n or nj < 0 or nj >= m:
                    continue 
                if d1[ni][nj] != inf:
                    continue 
                if grid[ni][nj] == '#':
                    continue 
                d1[ni][nj] = d1[i][j] + 1
                q.append([ni,nj]) 
        
        if d1[sx][sy] == inf: #特判终点无法到达起点的情况,此时返回-1
            return -1 

        vis = [[-1] * m for _ in range(n)]
        #能在[附加负面效果]的情况下,移动不超过maxDis步,到达终点
        def check(maxDis:int) -> bool:
            
            def dfs(i: int, j: int) -> bool:
                if i < 0 or i >= n or j < 0 or j >= m or vis[i][j] == maxDis or grid[i][j] == '#':
                    return False 
                if grid[i][j] == 'T': #[i,j] == enode:
                    return True 
                vis[i][j] = maxDis
                if grid[i][j] == '.' and (grid[n-1-i][j] != '#' and d1[n-1-i][j] > maxDis or grid[i][m-1-j] != '#' and d1[i][m-1-j] > maxDis): #注意需要加上grid[i][j] == '.'
                    return False #守卫在(i,j)处使用卷轴
                for k in range(4):
                    ni = i + dirs[k][0]
                    nj = j + dirs[k][1]
                    if dfs(ni,nj):
                        return True 
                return False 

            return dfs(sx,sy)

        res = -1
        left = 0
        right = m * n + 1
        while left <= right:
            mid = (left + right) // 2
            if check(mid):
                res = mid 
                right = mid - 1
            else:
                left = mid + 1
        return res 

        # ans = bisect_left(range(n*m+1),True,key=check)
        # return -1 if ans > n*m else ans 
#样例回答错误
class Solution:
    def challengeOfTheKeeper(self, grid: List[str]) -> int:
        n = len(grid)
        m = len(grid[0])
        dirs = [[-1,0],[0,-1],[1,0],[0,1]]
        snode = [-1,-1]
        enode = [-1,-1]
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 'S':
                    snode = [i,j]
                elif grid[i][j] == 'T':
                    enode = [i,j]
        
        #从终点到(i,j)的最小距离
        #INF = int(1e9)
        d1 = [[-1] * m for _ in range(n)] 
        d1[enode[0]][enode[1]] = 0
        q = collections.deque([enode])
        while len(q) > 0:
            i, j = q.popleft()
            for k in range(4):
                ni = i + dirs[k][0]
                nj = j + dirs[k][1]
                if ni < 0 or ni >= n or nj < 0 or nj >= m:
                    continue 
                if d1[ni][nj] != -1:
                    continue 
                if grid[ni][nj] == '#':
                    continue 
                d1[ni][nj] = d1[i][j] + 1
                q.append([ni,nj]) 
        
        #能在[附加负面效果]的情况下,移动不超过maxDis步,到达终点
        def check(maxDis:int) -> bool:
            q = collections.deque([snode])
            vis = [[False] * m for _ in range(n)]
            vis[snode[0]][snode[1]] = True
            while len(q) > 0:
                i, j = q.popleft()
                if [i,j] == enode:
                    return True 
                #if (grid[i][m-1-j] != '#' and (d1[i][m-1] == -1 or d1[i][m-1-j] > maxDis)) or (grid[n-1-i][j] != '#' and (d1[n-1-i][j] == -1 or d1[n-1-i][j] > maxDis)):
                    #return False #守卫者在(i,j)处使用卷轴
                for k in range(4):
                    ni = i + dirs[k][0]
                    nj = j + dirs[k][1]
                    if ni < 0 or ni >= n or nj < 0 or nj >= m:
                        continue 
                    if grid[ni][nj] == '#':
                        continue 
                    if vis[ni][nj]:
                        continue 
                    if (grid[ni][m-1-nj] != '#' and (d1[ni][m-1-nj] == -1 or d1[ni][m-1-nj] > maxDis)) or (grid[n-1-ni][nj] != '#' and (d1[n-1-ni][nj] == -1 or d1[n-1-ni][nj] > maxDis)):
                        continue 
                    q.append([ni,nj])
                    vis[ni][nj] = True 
            return False 
        
        res = -1
        left = 0
        right = m * n + 1
        while left <= right:
            mid = (left + right) // 2
            if check(mid):
                res = mid 
                right = mid - 1
            else:
                left = mid + 1
        return res 
#mine,有case回答不对
class Solution:
    def challengeOfTheKeeper(self, grid: List[str]) -> int:
        #找到从S到T的最短路径上的点
        n = len(grid)
        m = len(grid[0])
        snode = [-1,-1]
        enode = [-1,-1]
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 'S':
                    snode = [i,j]
                elif grid[i][j] == 'T':
                    enode = [i,j]
        dirs = [[-1,0],[0,-1],[1,0],[0,1]]

        def bfs(sx: int, sy: int, ex: int, ey: int) -> list:
            q = collections.deque([])
            d = [[-1] * m for _ in range(n)]
            q.append([sx,sy])
            d[sx][sy] = 0
            while len(q) > 0:
                i,j = q.popleft()
                if i == ex and j == ey: #提前退出
                    break 
                for k in range(4):
                    ni = i + dirs[k][0]
                    nj = j + dirs[k][1]
                    if ni < 0 or ni >= n or nj < 0 or nj >= m:
                        continue 
                    if d[ni][nj] != -1:
                        continue 
                    if grid[ni][nj] == '#':
                        continue 
                    q.append([ni,nj])
                    d[ni][nj] = d[i][j] + 1
            return d 
        
        d1 = bfs(snode[0],snode[1],enode[0],enode[1])
        paths = [] #最短路径上的点
        q = [enode]
        paths = collections.defaultdict(list) #路径上的点
        #paths[d1[enode[0]][enode[1]]].append(enode) #去掉终点
        while len(q) > 0:
            nq = len(q)
            dist = d1[q[0][0]][q[0][1]]
            while nq > 0:
                nq -= 1
                i,j = q.pop()
                for k in range(4):
                    ni = i + dirs[k][0]
                    nj = j + dirs[k][1]
                    if ni < 0 or ni >= n or nj < 0 or nj >= m:
                        continue 
                    if d1[ni][nj] != -1 and d1[ni][nj] == dist-1:
                        q.append([ni,nj])
                        paths[dist-1].append([ni,nj])
        
        #路径上每一个点的镜像点都是石头
        flag = True 
        
        #计算在点paths[i]处设置卷轴,的距离
        #首先计算终点到所有点的最小距离
        d2 = bfs(enode[0],enode[1],-1,-1)
        res = -1
        for key in paths:
            #计算这一层的最小值
            ans = int(1e9)
            for x1,y1 in paths[key]:
                #水平和竖直中计算一个最大值
                t = -1 
                #水平对称的点
                x2 = n-1-x1 
                y2 = y1 

                if grid[x2][y2] != '#' and d2[x2][y2] != -1:
                    t = max(t, d2[x2][y2])

                #竖直对称的点
                x3 = x1 
                y3 = m-1-y1 

                if grid[x3][y3] != '#' and d2[x3][y3] != -1:
                    t = max(t, d2[x3][y3])
                
                #与ans比较,求最小
                if t != -1:
                    ans = min(ans, t)

                if grid[x2][y2] == '.' or grid[x3][y3] == '.':
                    flag = False 
            
            #print(f"key = {key}, ans = {ans}!")

            if ans == int(1e9):
                pass 
            else:
                #计算最终答案的最大值
                res = max(res, ans)
        if flag: #特判路径上的点的镜像点都是石头,并且能从起点走到终点。此时,无法使用卷轴,在[附加负面效果]下的移动距离为0
            if d1[enode[0]][enode[1]] != -1:
                return 0 
        return res 
        

题目521728. 猫和老鼠 II

解题思路:dfs。

C++代码如下,

class Solution {
public:
    bool canMouseWin(vector<string>& grid, int catJump, int mouseJump) {
        int n = grid.size();
        int m = grid[0].size();
        int dirs[4][2] = {{-1,0},{0,-1},{1,0},{0,1}};
        pair<int,int> cat, mouse, food;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                if (grid[i][j] == 'C') cat = make_pair(i,j);
                else if (grid[i][j] == 'M') mouse = make_pair(i,j);
                else if (grid[i][j] == 'F') food = make_pair(i,j);
            }
        }

        int memo[10][10][10][10][130];
        memset(memo, -1, sizeof memo);
        function<bool(pair<int,int>, pair<int,int>, int)> dfs = [&] (pair<int,int> a, pair<int,int> b, int cnt) -> bool {
            int& f = memo[a.first][a.second][b.first][b.second][cnt];
            if (f != -1) return f;

            //a表示老鼠,b表示猫,cnt表示回合
            //return true表示老鼠获胜。否则,老鼠不获胜。
            if (a == b || b == food || cnt > 128) return f = false;
            if (a == food) return f = true;
            bool is_cat = false;
            pair<int,int> pos;
            int jump;

            if (cnt % 2) {
                is_cat = true;
                pos = b;
                jump = catJump;
            } else {
                pos = a;
                jump = mouseJump;
            }

            for (int k = 0; k < 4; ++k) {
                for (int jp = 0; jp < jump+1; ++jp) {
                    int ni = pos.first + dirs[k][0] * jp;
                    int nj = pos.second + dirs[k][1] * jp;
                    if (ni < 0 || ni >= n || nj < 0 || nj >= m) break;
                    if (grid[ni][nj] == '#') break;

                    if (!is_cat && dfs(make_pair(ni,nj), b, cnt+1)) return f = true;
                    
                    if (is_cat && !dfs(a, make_pair(ni,nj), cnt+1)) return f = false;
                }
            }
            return f = is_cat;
        };
        return dfs(mouse, cat, 0);
    }
};

python3代码如下,

class Solution:
    def canMouseWin(self, grid: List[str], catJump: int, mouseJump: int) -> bool:
        n, m = len(grid), len(grid[0])
        dirs = [[0,1],[1,0],[0,-1],[-1,0]]
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 'C':
                    cat = (i,j)
                elif grid[i][j] == 'M':
                    mouse = (i,j)
                elif grid[i][j] == 'F':
                    food = (i,j)
        @cache 
        def dfs(a: tuple, b: tuple, cnt: int) -> bool:
            #a表示老鼠,b表示猫,cnt表示每轮回合
            #return True表示老鼠获胜
            #return False表示老鼠不获胜
            if a == b or b == food or cnt > 128:
                return False 
            if a == food:
                return True 
            is_cat = False 
            if cnt % 2:
                is_cat = True
                pos, jump = b, catJump 
            else:
                pos, jump = a, mouseJump
            i,j = pos   
            for k in range(4):
                for dd in range(jump+1):
                    ni = i + dirs[k][0] * dd 
                    nj = j + dirs[k][1] * dd 
                    if ni < 0 or ni >= n or nj < 0 or nj >= m:
                        break  
                    if grid[ni][nj] == '#':
                        break 
                    if not is_cat and dfs((ni,nj), b, cnt+1):
                        return True 
                    elif is_cat and not dfs(a, (ni,nj), cnt+1): #只要存在一种情况失败,那就是猫获胜
                        return False 
            return is_cat
        return dfs(mouse, cat, 0)           
#参考答案
DIRS = (0, 1), (1, 0), (0, -1), (-1, 0)
class Solution:
    def canMouseWin(self, grid: List[str], catJump: int, mouseJump: int) -> bool:
        mm, nn = len(grid), len(grid[0])
        for x in range(mm):
            for y in range(nn):
                match grid[x][y]:
                    case 'C':
                        cat = x, y
                    case 'F':
                        food = x, y
                    case 'M':
                        mouse = x, y

        @cache
        def dfs(m, c, i):
            """
            极大极小博弈,
            老鼠尽量找自己获胜的,其次接受平局
            猫尽量找自己获胜的,其次接受平局

            :param m: 老鼠的位置
            :param c: 猫的位置
            :param i: 回合
            """
            if m == c or c == food or i > 128:
                return False
            if m == food:
                return True
            is_cat = False
            # 猫回合
            if i % 2:
                pos, jump = c, catJump
                is_cat = True
            else:
                pos, jump = m, mouseJump
            for dx, dy in DIRS:
                for jp in range(jump + 1):
                    nx, ny = pos[0] + dx * jp, pos[1] + dy * jp
                    if nx < 0 or ny < 0 or nx >= mm or ny >= nn or grid[nx][ny] == '#':
                        break
                    if not is_cat and dfs((nx, ny), c, i + 1):
                        return True
                    elif is_cat and not dfs(m, (nx, ny), i + 1):
                        return False
            return is_cat
        
        return dfs(mouse, cat, 0)

3 参考

灵神力扣题单