diff --git a/src/main/java/org/gd/common/Commons.java b/src/main/java/org/gd/common/Commons.java index b0e3536e..56216f00 100644 --- a/src/main/java/org/gd/common/Commons.java +++ b/src/main/java/org/gd/common/Commons.java @@ -357,7 +357,7 @@ public static boolean isPowerOfTwo(long n) { } public static int digitsCount(int num) { - return num == 0 ? 1 : (int) (Math.log10(Math.abs(num)) + 1); + return num == 0 ? 1 : (int) (Math.log10(Math.abs(num)) + (num < 0 ? 2 : 1)); } public static int maxDigitsCount(int[] arr) { diff --git a/src/main/java/org/gd/leetcode/p0741/BlockPoint.java b/src/main/java/org/gd/leetcode/p0741/BlockPoint.java new file mode 100644 index 00000000..29a63125 --- /dev/null +++ b/src/main/java/org/gd/leetcode/p0741/BlockPoint.java @@ -0,0 +1,39 @@ +package org.gd.leetcode.p0741; + +public class BlockPoint implements Point { + + static final org.gd.leetcode.p0741.BlockPoint INSTANCE = new org.gd.leetcode.p0741.BlockPoint(); + + private BlockPoint() {} + + @Override + public int value() { return -1; } + + @Override + public int pathSum() { + throw new UnsupportedOperationException(new String(new char[]{175, 92, 95, 40, 12_484, 41, 95, 47, 175})); + } + + @Override + public boolean isBlock() { return true; } + + @Override + public boolean isValid() { return false; } + + @Override + public void addPrev(Point prev) { + throw new UnsupportedOperationException(new String(new char[]{175, 92, 95, 40, 12_484, 41, 95, 47, 175})); + } + + @Override + public void reset() {} + + @Override + public void erase() {} + + @Override + public String path() { return ""; } + + @Override + public String toString() { return "(block)"; } +} diff --git a/src/main/java/org/gd/leetcode/p0741/Point.java b/src/main/java/org/gd/leetcode/p0741/Point.java new file mode 100644 index 00000000..20d5846b --- /dev/null +++ b/src/main/java/org/gd/leetcode/p0741/Point.java @@ -0,0 +1,25 @@ +package org.gd.leetcode.p0741; + +interface Point { + + static Point of(int row, int col, int value) { + return value == -1 ? BlockPoint.INSTANCE : new PointImpl(row, col, value); + } + + int value(); + + int pathSum(); + + boolean isBlock(); + + boolean isValid(); + + void addPrev(Point prev); + + void reset(); + + void erase(); + + String path(); + +} diff --git a/src/main/java/org/gd/leetcode/p0741/PointImpl.java b/src/main/java/org/gd/leetcode/p0741/PointImpl.java new file mode 100644 index 00000000..8f7755e1 --- /dev/null +++ b/src/main/java/org/gd/leetcode/p0741/PointImpl.java @@ -0,0 +1,80 @@ +package org.gd.leetcode.p0741; + +public class PointImpl implements Point { + + private final int row, col; + + private int value; + private int sum; + private Point prev; + private Boolean valid; + + PointImpl(int row, int col, int value) { + this.row = row; + this.col = col; + this.value = value; + sum = value; + } + + @Override + public int value() { return value; } + + @Override + public int pathSum() { return sum; } + + @Override + public boolean isBlock() { return false; } + + @Override + public boolean isValid() { + if (valid != null) + return valid; + + if (row == 0 && col == 0) + return valid = true; + + if (prev != null) + return valid = prev.isValid(); + + return valid = false; + } + + @Override + public void addPrev(Point prev) { + if (prev.isBlock()) return; + reset(); + this.prev = prev; + sum += prev.pathSum(); + isValid(); + } + + @Override + public void reset() { + sum = value; + if (prev != null) { + prev.reset(); + prev = null; + } + } + + @Override + public void erase() { + value = 0; + sum = 0; + if (prev != null) { + prev.erase(); + prev = null; + } + } + + @Override + public String path() { + String s = String.format("(%d;%d)", row, col); + if (prev != null) + s = String.format("%s->%s", prev.path(), s); + return s; + } + + @Override + public String toString() { return "" + value; } +} diff --git a/src/main/java/org/gd/leetcode/p0741/Solution.java b/src/main/java/org/gd/leetcode/p0741/Solution.java new file mode 100644 index 00000000..09727d35 --- /dev/null +++ b/src/main/java/org/gd/leetcode/p0741/Solution.java @@ -0,0 +1,117 @@ +package org.gd.leetcode.p0741; + +import org.gd.leetcode.common.LeetCode; + +/** + * https://leetcode.com/problems/cherry-pickup/ + * + * @author Horkhover D. + * @since 2020-07-16 + */ +@LeetCode( + name = "Cherry Pickup", + difficulty = LeetCode.Level.HARD, + state = LeetCode.State.TODO, + tags = LeetCode.Tags.DYNAMIC_PROGRAMMING +) +class Solution { + + private Point[][] grid; + private int rows, cols; + + private void reset(int[][] grid) { + rows = grid.length; + cols = grid[0].length; + this.grid = new Point[rows][cols]; + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + this.grid[row][col] = Point.of(row, col, grid[row][col]); + } + } + } + + private int forward() { + + for (int row = 1; row < rows && !grid[row][0].isBlock(); row++) + grid[row][0].addPrev(grid[row - 1][0]); + + for (int col = 1; col < cols && !grid[0][col].isBlock(); col++) + grid[0][col].addPrev(grid[0][col - 1]); + + for (int row = 1; row < rows; row++) { + for (int col = 1; col < cols; col++) { + + if (grid[row][col].isBlock()) + continue; + + final Point up = grid[row - 1][col]; + final Point left = grid[row][col - 1]; + + if (up.isBlock()) { + if (!left.isBlock()) { + grid[row][col].addPrev(left); + } + } else if (left.isBlock()) { + grid[row][col].addPrev(up); + } else if (up.value() > left.value()) { + grid[row][col].addPrev(up); + } else { + grid[row][col].addPrev(left); + } + } + } + + Point point = grid[rows - 1][cols - 1]; + if (point.isValid()) { + int value = point.pathSum(); + point.erase(); + return value; + } + + return -1; + } + + private int backward() { + + for (int row = rows - 2; row >= 0 && !grid[row][cols - 1].isBlock(); row--) + grid[row][cols - 1].addPrev(grid[row + 1][cols - 1]); + + for (int col = cols - 2; col >= 0 && !grid[rows - 1][col].isBlock(); col--) + grid[rows - 1][col].addPrev(grid[rows - 1][col + 1]); + + for (int row = rows - 2; row >= 0; row--) { + for (int col = cols - 2; col >= 0; col--) { + Point current = grid[row][col]; + current.reset(); + if (current.isBlock()) + continue; + + Point right = grid[row][col + 1]; + Point down = grid[row + 1][col]; + if (right.isBlock()) { + if (!down.isBlock()) + current.addPrev(down); + } else if (down.isBlock()) { + current.addPrev(right); + } else if (right.value() > down.value()) { + current.addPrev(right); + } else { + current.addPrev(down); + } + } + } + + return grid[0][0].pathSum(); + } + + public int cherryPickup(int[][] intsGrid) { + reset(intsGrid); + + final int forward = forward(); + if (forward == -1) + return 0; + + return forward + backward(); + } + +} diff --git a/src/test/java/org/gd/leetcode/p0741/SolutionTest.java b/src/test/java/org/gd/leetcode/p0741/SolutionTest.java new file mode 100644 index 00000000..ce0eb807 --- /dev/null +++ b/src/test/java/org/gd/leetcode/p0741/SolutionTest.java @@ -0,0 +1,57 @@ +package org.gd.leetcode.p0741; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test for {@link Solution} + * + * @author Horkhover D. + * @since 2020-07-16.07.2020 + */ +@DisplayName("LeetCode #741: Cherry Pickup") +class SolutionTest { + + private static List args() { + return List.of( + + Arguments.of(new int[][]{ + {1, 1, 1, 1, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 1}, + {1, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 1, 1, 1, 1}}, 15), + + Arguments.of(new int[][]{ + {1, 1, -1}, + {1, -1, 1}, + {-1, 1, 1}}, 0), + + Arguments.of(new int[][]{ + {0, 1, -1}, + {1, 0, -1}, + {1, 1, 1}}, 5), + + Arguments.of(new int[][]{ + {0, 1, -1, 1}, + {1, 0, -1, 1}, + {1, 1, 1, 0}}, 5) + ); + } + + @ParameterizedTest + @MethodSource("args") + @DisplayName("CherryPickup") + void test_CherryPickup(int[][] grid, int expected) { + assertEquals(expected, new Solution().cherryPickup(grid)); + } +} \ No newline at end of file