問題ページはこちら。
2553. Separate the Digits in an Array
与えられた数値を1桁ずつ返す問題。
整数で与えられるので、文字列として解釈して1文字ずつ解を得ればOK。
class Solution:
def separateDigits(self, nums: List[int]) -> List[int]:
ans = []
for num in nums:
for char in str(num):
ans.append(int(char))
return ans
2554. Maximum Number of Integers to Choose From a Range I
小さい値から貪欲に取っていく。
bannedはリストで与えられるが、重複があるかもしれないのと、x in bannedは計算量がO(N)で遅いのでsetに直す。
class Solution:
def maxCount(self, banned: List[int], n: int, maxSum: int) -> int:
banned = set(banned)
ans = 0
for i in range(1, n+1):
if i in banned:
continue
if maxSum >= i:
maxSum -= i
ans += 1
else:
break
return ans
2555. Maximize Win From Two Segments
ある範囲が与えられ、長さk以下の区間を2個選んだ時に得られる個数の最大値を答える問題。
左右から尺取法をしていって答えを求める。
[0, i]と[i+1, n]のそれぞれの最大値の和が最大になれば良い。
答えを求めるための最後のループは配列外参照に注意。
class Solution:
def maximizeWin(self, prizePositions: List[int], k: int) -> int:
n = len(prizePositions)
left = [0] * n
left[0] = 1
j = 0
for i in range(1, n):
while prizePositions[i] - prizePositions[j] > k:
j += 1
left[i] = max(left[i-1], i-j+1)
right = [0] * n
right[n-1] = 1
j = n-1
for i in range(n-2, -1, -1):
while prizePositions[j] - prizePositions[i] > k:
j -= 1
right[i] = max(right[i+1], j-i+1)
ans = 0
for i in range(n):
ans = max(ans, left[i] + (right[i+1] if i != n-1 else 0))
return ans
左からの尺取でO(N)、右からの尺取でO(N)、答えを求める計算がO(N)かかるので、計算量はO(3N)=O(N)となる。
ここで、尺取を1回行うだけで解けないか考えてみる。
[left, right]が1つの候補に挙がる時、もう1つの候補は[0, left – 1]で得られる最大値を足せば良いことに気づく。
i番目までに得られる最大値をdp[i]とすると
尺取中の範囲[left, right] + dp[left – 1]が答えになる。
0-indexで+1するかどうか注意しながら実装。
class Solution:
def maximizeWin(self, prizePositions: List[int], k: int) -> int:
n = len(prizePositions)
dp = [0] * (n + 1)
ans = 0
l = 0
for r in range(n):
while prizePositions[r] - prizePositions[l] > k:
l += 1
dp[r + 1] = max(dp[r], r - l + 1)
ans = max(ans, r - l + 1 + dp[l])
return ans
O(N)1回分の計算量で解くことができた。
2556. Disconnect Path in a Binary Matrix by at Most One Flip
グラフの1箇所だけを通れなくして、ゴールまでたどり着けるかどうか判定する問題。
ゴールまでたどり着くには複数の経路があるはずだが、全ての経路で共通して通る1マスが存在しているかどうか見つければいい。
その1マスを通れなくすれば、全ての経路でゴールまでたどり着けなくなる。
前処理として、そもそもゴールまで到達可能かどうか調べる。
class Solution:
def isPossibleToCutPath(self, grid: List[List[int]]) -> bool:
m = len(grid)
n = len(grid[0])
reachable = [[False] * n for _ in range(m)]
reachable[0][0] = True
for i in range(1, m):
reachable[i][0] = reachable[i-1][0]
for i in range(1, n):
reachable[0][i] = reachable[0][i-1]
for i in range(1, m):
for j in range(1, n):
if grid[i][j] == 1:
reachable[i][j] = reachable[i-1][j] or reachable[i][j-1]
if not reachable[m-1][n-1]:
return True
その後はBFSをしていく。
全てのマスで共通して通る1マスがあれば、キューにある座標が1個だけになるはずである。
ただし、スタートとゴールがキューにあると同じく個数が1個になるので注意する。
visited = set()
visited.add((0, 0))
q = deque()
q.append((0,0))
while q:
if len(q) == 1 and q[0] != (0,0) and q[0] != (m-1, n-1):
return True
for _ in range(len(q)):
row, col = q.popleft()
for i, j in [(1,0),(0,1)]:
if row + i == m or col + j == n:
continue
if grid[row+i][col+j] == 1 and (row+i, col+j) not in visited:
q.append((row+i, col+j))
visited.add((row+i, col+j))
return False
別解として、ゴールから逆算する方法を考える。
スタートからゴールに行けるということは、ゴールからスタートにも行けるはずである。
ここで、スタートから座標(i,j)に到達する経路の数をforward[i][j]、ゴールからの経路の数をbackward[i][j]、全ての経路の数をtotalとする。
BFSを使った解法と同じく、もし全ての経路が共通で通る1マスが存在する場合、total = forward[i][j] * backward[i][j]が成り立つことを利用する。
噛み砕いて考えると、A→Bに行く方法が2通り、B→Cに行く方法が3通りであれば、A→Cに行く方法は6通りになる、という計算と同じになる。
二次元配列で添字が増えるとややこしいが、アイデア自体は単純なはず。
class Solution:
def isPossibleToCutPath(self, grid: List[List[int]]) -> bool:
m = len(grid)
n = len(grid[0])
forward = [[0] * n for _ in range(m)]
forward[0][0] = 1
for i in range(m):
for j in range(n):
if i > 0 and grid[i][j] == 1:
forward[i][j] += forward[i-1][j]
if j > 0 and grid[i][j] == 1:
forward[i][j] += forward[i][j-1]
backward = [[0] * n for _ in range(m)]
backward[m-1][n-1] = 1
for i in range(m-1, -1, -1):
for j in range(n-1, -1, -1):
if i < m-1 and grid[i][j] == 1:
backward[i][j] += backward[i+1][j]
if j < n-1 and grid[i][j] == 1:
backward[i][j] += backward[i][j+1]
total = forward[m-1][n-1]
if total == 0:
return True
for i in range(m):
for j in range(n):
if forward[i][j] * backward[i][j] == total:
if (i,j) != (0,0) and (i,j) != (m-1,n-1):
return True
return False
コメント