1 介绍
本博客用来记录灵神力扣题单之网格图。
2 训练
2.5 综合应用
题目50:LCP 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
题目51:LCP 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
题目52:1728. 猫和老鼠 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)