Skip to content

Commit 44e2775

Browse files
committed
773 Sliding Puzzle
1 parent 25f7ea7 commit 44e2775

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

755 Pour Water.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/python3
2+
"""
3+
We are given an elevation map, heights[i] representing the height of the terrain
4+
at that index. The width at each index is 1. After V units of water fall at
5+
index K, how much water is at each index?
6+
7+
Water first drops at index K and rests on top of the highest terrain or water at
8+
that index. Then, it flows according to the following rules:
9+
10+
If the droplet would eventually fall by moving left, then move left.
11+
Otherwise, if the droplet would eventually fall by moving right, then move right.
12+
Otherwise, rise at it's current position.
13+
Here, "eventually fall" means that the droplet will eventually be at a lower
14+
level if it moves in that direction. Also, "level" means the height of the
15+
terrain plus any water in that column.
16+
We can assume there's infinitely high terrain on the two sides out of bounds of
17+
the array. Also, there could not be partial water being spread out evenly on
18+
more than 1 grid block - each unit of water has to be in exactly one block.
19+
20+
Example 1:
21+
Input: heights = [2,1,1,2,1,2,2], V = 4, K = 3
22+
Output: [2,2,2,3,2,2,2]
23+
Explanation:
24+
# #
25+
# #
26+
## # ###
27+
#########
28+
0123456 <- index
29+
30+
The first drop of water lands at index K = 3:
31+
32+
# #
33+
# w #
34+
## # ###
35+
#########
36+
0123456
37+
38+
When moving left or right, the water can only move to the same level or a lower
39+
level.
40+
(By level, we mean the total height of the terrain plus any water in that column.)
41+
Since moving left will eventually make it fall, it moves left.
42+
(A droplet "made to fall" means go to a lower height than it was at previously.)
43+
44+
# #
45+
# #
46+
## w# ###
47+
#########
48+
0123456
49+
50+
Since moving left will not make it fall, it stays in place. The next droplet
51+
falls:
52+
53+
# #
54+
# w #
55+
## w# ###
56+
#########
57+
0123456
58+
59+
Since the new droplet moving left will eventually make it fall, it moves left.
60+
Notice that the droplet still preferred to move left,
61+
even though it could move right (and moving right makes it fall quicker.)
62+
63+
# #
64+
# w #
65+
## w# ###
66+
#########
67+
0123456
68+
69+
# #
70+
# #
71+
##ww# ###
72+
#########
73+
0123456
74+
75+
After those steps, the third droplet falls.
76+
Since moving left would not eventually make it fall, it tries to move right.
77+
Since moving right would eventually make it fall, it moves right.
78+
79+
# #
80+
# w #
81+
##ww# ###
82+
#########
83+
0123456
84+
85+
# #
86+
# #
87+
##ww#w###
88+
#########
89+
0123456
90+
91+
Finally, the fourth droplet falls.
92+
Since moving left would not eventually make it fall, it tries to move right.
93+
Since moving right would not eventually make it fall, it stays in place:
94+
95+
# #
96+
# w #
97+
##ww#w###
98+
#########
99+
0123456
100+
101+
The final answer is [2,2,2,3,2,2,2]:
102+
103+
#
104+
#######
105+
#######
106+
0123456
107+
Example 2:
108+
Input: heights = [1,2,3,4], V = 2, K = 2
109+
Output: [2,3,3,4]
110+
Explanation:
111+
The last droplet settles at index 1, since moving further left would not cause
112+
it to eventually fall to a lower height.
113+
Example 3:
114+
Input: heights = [3,1,3], V = 5, K = 1
115+
Output: [4,4,4]
116+
Note:
117+
118+
heights will have length in [1, 100] and contain integers in [0, 99].
119+
V will be in range [0, 2000].
120+
K will be in range [0, heights.length - 1].
121+
"""
122+
from typing import List
123+
124+
125+
class Solution:
126+
def pourWater(self, heights: List[int], V: int, K: int) -> List[int]:
127+
"""
128+
Simulation?
129+
O(V * L)
130+
"""
131+
for _ in range(V):
132+
s = K
133+
# looking to the left
134+
optimal = s
135+
for i in range(s-1, -1, -1):
136+
if heights[i] <= heights[i+1]:
137+
if heights[i] < heights[optimal]:
138+
optimal = i
139+
else:
140+
break
141+
if optimal == s:
142+
# looking to the right
143+
for i in range(s+1, len(heights)):
144+
if heights[i] <= heights[i-1]:
145+
if heights[i] < heights[optimal]:
146+
optimal = i
147+
else:
148+
break
149+
heights[optimal] += 1
150+
151+
return heights
152+
153+
154+
if __name__ == "__main__":
155+
assert Solution().pourWater([2,1,1,2,1,2,2], 4, 3) == [2,2,2,3,2,2,2]

773 Sliding Puzzle.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#!/usr/bin/python3
2+
"""
3+
On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and
4+
an empty square represented by 0.
5+
6+
A move consists of choosing 0 and a 4-directionally adjacent number and swapping
7+
it.
8+
9+
The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]].
10+
11+
Given a puzzle board, return the least number of moves required so that the
12+
state of the board is solved. If it is impossible for the state of the board to
13+
be solved, return -1.
14+
15+
Examples:
16+
17+
Input: board = [[1,2,3],[4,0,5]]
18+
Output: 1
19+
Explanation: Swap the 0 and the 5 in one move.
20+
Input: board = [[1,2,3],[5,4,0]]
21+
Output: -1
22+
Explanation: No number of moves will make the board solved.
23+
Input: board = [[4,1,2],[5,0,3]]
24+
Output: 5
25+
Explanation: 5 is the smallest number of moves that solves the board.
26+
An example path:
27+
After move 0: [[4,1,2],[5,0,3]]
28+
After move 1: [[4,1,2],[0,5,3]]
29+
After move 2: [[0,1,2],[4,5,3]]
30+
After move 3: [[1,0,2],[4,5,3]]
31+
After move 4: [[1,2,0],[4,5,3]]
32+
After move 5: [[1,2,3],[4,5,0]]
33+
Input: board = [[3,2,4],[1,5,0]]
34+
Output: 14
35+
Note:
36+
37+
board will be a 2 x 3 array as described above.
38+
board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5].
39+
"""
40+
from typing import List
41+
from collections import defaultdict
42+
from copy import deepcopy
43+
import heapq
44+
45+
46+
final_pos = {
47+
1: (0, 0),
48+
2: (0, 1),
49+
3: (0, 2),
50+
4: (1, 0),
51+
5: (1, 1),
52+
0: (1, 2),
53+
}
54+
55+
dirs = (
56+
(0, -1),
57+
(0, 1),
58+
(-1, 0),
59+
(1, 0),
60+
)
61+
62+
63+
class Solution:
64+
def slidingPuzzle(self, board: List[List[int]]) -> int:
65+
"""
66+
BFS + visited
67+
68+
=> A*
69+
priority = current_dist + heuristic_dist
70+
71+
Chain the matrix into 1d array. N = R * C
72+
Complexity O(N * N!)
73+
There are O(N!) possible board states. O(N) is the time to scan the board for the operations in the loop.
74+
"""
75+
visited = defaultdict(bool)
76+
m, n = len(board), len(board[0])
77+
q = [(self.heuristic_dist(board) + 0, 0, board)]
78+
target = [
79+
[1, 2, 3],
80+
[4, 5, 0],
81+
]
82+
while q:
83+
heu, cur_dist, board = heapq.heappop(q)
84+
visited[self.ser(board)] = True
85+
if board == target:
86+
return cur_dist
87+
88+
cur_dist += 1
89+
i, j = self.zero_pos(board)
90+
for di, dj in dirs:
91+
I = i + di
92+
J = j + dj
93+
if 0 <= I < m and 0 <= J < n:
94+
B = deepcopy(board) # need a copy in the queue
95+
B[I][J], B[i][j] = B[i][j], B[I][J]
96+
if not visited[self.ser(B)]:
97+
heapq.heappush(q, (self.heuristic_dist(B) + cur_dist, cur_dist, B))
98+
return -1
99+
100+
def zero_pos(self, board):
101+
for i, row in enumerate(board):
102+
for j, v in enumerate(row):
103+
if v == 0:
104+
return i, j
105+
raise
106+
107+
def heuristic_dist(self, board):
108+
"""
109+
manhattan distance
110+
"""
111+
ret = 0
112+
for i, row in enumerate(board):
113+
for j, v in enumerate(row):
114+
if v != 0:
115+
I, J = final_pos[v]
116+
ret += abs(i - I) + abs(j - J)
117+
return ret
118+
119+
def ser(self, board):
120+
return tuple(
121+
tuple(row)
122+
for row in board
123+
)
124+
125+
126+
if __name__ == "__main__":
127+
assert Solution().slidingPuzzle([[1,2,3],[4,0,5]]) == 1
128+
assert Solution().slidingPuzzle([[1,2,3],[5,4,0]]) == -1

0 commit comments

Comments
 (0)