1。
Paper Cut into Minimum Number of Squares
Given a paper of size A x B. Task is to cut the paper into squares of any size. Find the minimum number of squares that can be cut from the paper.
Examples:
Input : 36 x 30
Output : 5
Explanation : 3 (squares of size 12x12) + 2 (squares of size 18x18)
Input : 4 x 5
Output : 5
Explanation : 1 (squares of size 4x4) + 4 (squares of size 1x1)
Wrong Sol : Greedy solution doesn’t always produce optimal result.
For example if input is 36 x 30,
Greedy algorithm:
-
One square of size 30 x 30
-
Five squares of size 6 x 6
would produce output 6, but we can cut the paper in 5 squares,
-
Three squares of size 12 x 12
-
Two squares of size 18 x 18
Sol1 : DFS
红色分叉表示切在width上
蓝色分叉表示切在height上
黄色表示base case
绿色表示重复计算
public int dfsWithoutMem(int width, int height) { if (width == height) { return 1; } int vertMin = Integer.MAX_VALUE; int horMin = Integer.MAX_VALUE; for (int i = 1; i <= width / 2; i++) { vertMin = Math.min(dfsWithoutMem(width - i, height) + dfsWithoutMem(i, height), vertMin); } for (int j = 1; j <= height / 2; j++) { horMin = Math.min(dfsWithoutMem(width, height - j) + dfsWithoutMem(width, j), horMin); } int res = Math.min(vertMin, horMin); return res; }
Sol2 : DFS apply memorization: map version public int dfsWithMemI(int width, int height) { Map<String, Integer> cache = new HashMap<>(); return dfsWithMemHelperI(width, height, cache); } private int dfsWithMemHelperI(int width, int height, Map<String, Integer> cache) { if (width % height == 0) { return width / height; } if (height % width == 0) { return height / width; } String key = Math.min(width, height) + "," + Math.max(width, height); Integer minNumber = cache.get(key); if (minNumber != null) { return minNumber; } int vertMin = Integer.MAX_VALUE; int horMin = Integer.MAX_VALUE; for (int i = 1; i <= width / 2; i++) { vertMin = Math.min(dfsWithMemHelperI(width - i, height, cache) + dfsWithMemHelperI(i, height, cache), vertMin); } for (int j = 1; j <= height / 2; j++) { horMin = Math.min(dfsWithMemHelperI(width, height - j, cache) + dfsWithMemelperI(width, j, cache), horMin); } int res = Math.min(vertMin, horMin); cache.put(key, res); return res; } Sol3 : DFS apply memorization: 2D matrix version public int dfsWithMemII(int width, int height) { int[][] dp = new int[width + 1][height + 1]; return dfsWithMemHelperII(width, height, dp); } private int dfsWithMemHelperII(int width, int height, int[][] dp) { if (width % height == 0) { return width / height; } if (height % width == 0) { return height / width; } if (dp[width][height] > 0) { return dp[width][height]; } int vertMin = Integer.MAX_VALUE; int horMin = Integer.MAX_VALUE; for (int i = 1; i <= width / 2; i++) { horMin = Math.min(dfsWithMemHelperII(i, height, dp) + dfsWithMemHelperII(width - i, height, dp), horMin); } for (int j = 1; j <= height / 2; j++) { vertMin = Math.min(dfsWithMemHelperII(width, j, dp) + dfsWithMemHelperII(width, height - j, dp), vertMin); } dp[width][height] = Math.min(horMin, vertMin); return dp[width][height]; }
Sol4 : DP
a. definition of DP state: using 2D dp, dp[i][j] represents the minimum number of squares
can be cut from paper with size i x j
note: dp[i][j] = dp[j][i] according to the physical meaning
b. induction rule: dp[i][j] = min(horizontal min, vertical min)
Try all possible horizontal and vertically splits
horizontal min = dp[i - x][j] + dp[x][j] x from [1, i / 2] (including)
vertically min = dp[i][j - x] + dp[i][x] x from [1, j / 2] (including)
c. base case: dp[i][i] = 1 i from [1, min(W, H)]
d. result: dp[W][H]
e. filling order: from left to right, up to bottom because we need to know all left and up values
f. optimization on space and time:
0 1 2 3 4
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
3 |
4 |
0 |
2 |
1 |
3 |
2 |
0 |
3 |
3 |
1 |
4 |
dp[3][4] = min(dp[1][4] + dp[2][4], dp[3][1] + dp[3][3], dp[3][2] + dp[3][2])
public int MinPaperCut(int width, int height) { int[][] dp = new int[width + 1][height + 1]; for (int x = 1; x < dp.length; x++) { for (int y = 1; y < dp[0].length; y++) { if (x == y) { //base case dp[x][y] = 1; } else { int vertical_min = Integer.MAX_VALUE; int horizontal_min = Integer.MAX_VALUE; for (int i = 1; i <= x / 2; i++) { horizontal_min = Math.min(horizontal_min, dp[x - i][y] + dp[i][y]); } for (int i = 1; i <= y / 2; i++) { vertical_min = Math.min(vertical_min, dp[x][y - i] + dp[x][i]); } dp[x][y] = Math.min(horizontal_min, vertical_min); } } } return dp[width][height]; } Time: O(WH * max(W, H)) since three for loops => WH * (W+H) Space: O(WH) since 2D array used
2.
有一个隧道,有一些雷达,雷达有不同的半径,问小车能否通过
————————————————————————x 5
.
————————————————————————y 0
vertex: 雷达 (i, j) + r
neighbors: 跟雷达有overlap的所有雷达们
在y的维度上需要一个interval(m, n): m = j-r, n = j+r
m >= x && n <= y 的时候: 小车不能通过
result boolean: yes or no pass the sui dao
connected component => 有重叠的雷达们 => interval (m, n) to represent the size of cc
List<Radius> radius; UnionFind uf = new UnionFind(); Map<Radius, List<Radius>> neighbors = helper(radius); // pre precessing : O(n) for (Radius r : radius) { // O(n) for (Radius nei : neighbors.get(r)) { // O(1) uf.union(nei, r); // ranking + path compression O(c) => O(1) // update size of connected component if (uf.cc.m >= x && uf.cc.m <= y) { return false; } } } return true;
是男人就跳问题 best first search
给一个二维坐标平面和一个起始点,从这点开始垂直下跳,下面有若干水平挡板,位置长度会在 input里给,板的形式是(x, y, distance),当跳到挡板上时,可以选择走到挡板的左端或者右端, 然后继续垂直下跳,直到落地位置,求从开始到落地需要走过的路程最短是多少。
5
|____|(i,j) 2,1 a
i - ib ib + d - i
1 |__|(i, jb)__________|2 b(ib, jb, d) ____ d
20 10
___________________________| c
__________________________________________地面
(i, j) + total cost reacing (i, j) pos
(i, j), cost
nei.cost = cur.cost + i - ib = cur cost + cur 到nei cost == nei cost
i, j + 10
(x, y, distance) => x是板子的左端点, y是板子的垂直位置,右端点 = x + distance
y -> y0表示左端点, y1表示右端点
a d b c a b d c
Queue<Point> q = new LinkedList<>();
q.offer(new Point(i, j, 0));
while (!q.isEmpty()) {
Point cur = q.poll();
for () {
}
}
是男人就跳问题
给一个二维坐标平面和一个起始点,从这点开始垂直下跳,下面有若干水平挡板,位置长度会在 input里给,板的形式是(x, y, distance),当跳到挡板上时,可以选择走到挡板的左端或者右端, 然后继续垂直下跳,直到落地位置,求从开始到落地需要走过的路程最短是多少。
5
|____|(i,j) 2,1 a
i - ib ib + d - i
1 |__|(i, jb)__________|2 b(ib, jb, d) ____ d
20 10
___________________________| c
__________________________________________地面
(i, j) + total cost reacing (i, j) pos
(i, j), cost
nei.cost = cur.cost + i - ib = cur cost + cur 到nei cost == nei cost
i, j + 10
(x, y, distance) => x是板子的左端点, y是板子的垂直位置,右端点 = x + distance
y -> y0表示左端点, y1表示右端点
a d b c a b d c
Queue<Point> q = new LinkedList<>();
q.offer(new Point(i, j, 0));
while (!q.isEmpty()) {
Point cur = q.poll();
for () {
}
}