class Solution {
int[][] g;
boolean[][] v;
int n, m;
int ans;
int[] startPoint;
int total;
int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public boolean isAvailable(int i, int j) {
if (i < 0 || j < 0 || i >= n || j >= m) return false;
if (g[i][j] == -1 || v[i][j]) return false;
return true;
}
public void go(int I, int J, int cur) {
// if (g[I][J] == 2) {
// if (cur == total) ans ++;
// v[I][J] = false;
// return;
// }
for (int k = 0; k < 4; k ++) {
int i = I + direction[k][0];
int j = J + direction[k][1];
if (isAvailable(i, j)) {
if (g[i][j] == 2) {
if (cur == total - 1) ans ++;
continue;
}
v[i][j] = true;
go(i, j, cur + 1);
v[i][j] = false;
}
}
}
public int uniquePathsIII(int[][] grid) {
if (grid == null) return 0;
n = grid.length;
if (n == 0) return 0;
m = grid[0].length;
if (m == 0) return 0;
ans = 0;
total = n * m;
g = grid;
v = new boolean[n][m];
startPoint = new int[2];
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
if (grid[i][j] == -1) total --;
else if (grid[i][j] == 1) {
startPoint[0] = i;
startPoint[1] = j;
}
}
}
v[startPoint[0]][startPoint[1]] = true;
go(startPoint[0], startPoint[1], 1);
return ans;
}
}
1478. 安排邮筒
原始解法
class Solution {
public int minDistance(int[] houses, int k) {
Arrays.sort(houses);
// dp[i][j][K]j...K个房子之间有i个邮箱的最小值
int[][][] dp = new int[k + 1][houses.length][houses.length];
// Arrays.fill(dp, Integer.MAX_VALUE);
for (int i = 0; i < dp.length; i ++) {
for (int j = 0; j < dp[i].length; j ++) {
Arrays.fill(dp[i][j], Integer.MAX_VALUE);
}
}
for (int i = 0; i <= k; i ++) {
for (int j = 0; j < houses.length; j ++) {
dp[i][j][j] = 0; // 区间段只有一个房子,一定是0
}
}
for (int K = 1; K < houses.length; K ++) {
for (int j = 0; j < K; j ++) {
int n = K - j + 1;
int mid = (K + j) / 2; // 0, 1, 2, 3
if ((n & 1) == 0) mid ++;
int sum = 0;
for (int i = j; i < mid; i ++) {
sum += houses[K - (i - j)] - houses[i];
}
dp[1][j][K] = sum; // n个房子有1个邮箱
for (int i = n; i <= k; i ++) dp[n][j][K] = 0; // n个房子有n+个邮箱,那一定是0
}
}
for (int i = 2; i <= k; i ++) {
for (int j = 0; j < houses.length; j ++) {
for (int d = 1; d + j < houses.length; d ++) { // d + 1个房之间有i个邮箱
int K = j + d;
// mid - j + 1 >= i - 1; j...mid之间的房子数量>=邮箱数量
for (int mid = j + i - 2; mid < K; mid ++) {
if (mid < j) continue;
dp[i][j][K] = Math.min(dp[i][j][K], dp[i - 1][j][mid] + dp[1][mid + 1][K]);
}
}
}
}
return dp[k][0][houses.length - 1];
}
}
改进解法
class Solution {
public int minDistance(int[] houses, int k) {
Arrays.sort(houses);
// midPlace[i][j]记录当把邮筒放在i,j相对中间的时候的距离和,相当于只有一个邮筒的最好解决方法
int[][] midPlace = new int[houses.length][houses.length];
for (int i = 0; i < houses.length; i ++) {
midPlace[i][i] = 0;
for (int d = 1; i + d < houses.length; d ++) {
int j = i + d;
int mid = (i + j) / 2; // 0, 1, 2, 3
int num = j - i + 1; // 区间内有num个房子
if ((num & 1) == 0) mid ++;
for (int K = 0; i + K < mid; K ++) {
midPlace[i][j] += houses[j - K] - houses[i + K];
}
}
}
// dp[i][j]记录到j为止使用了k个邮箱的最小值
int[][] dp = new int[k + 1][houses.length];
for (int i = 0; i <= k; i ++) {
Arrays.fill(dp[i], Integer.MAX_VALUE / 2);
}
for (int j = 0; j < houses.length; j ++) {
dp[1][j] = midPlace[0][j];
}
for (int i = 2; i <= k; i ++) {
for (int j = 0; j < houses.length; j ++) {
for (int m = 0; m < j; m ++) {
dp[i][j] = Math.min(dp[i][j], dp[i - 1][m] + midPlace[m + 1][j]);
}
}
}
return dp[k][houses.length - 1];
}
}
class Solution {
public boolean canIWin(int maxChoosableInteger, int desiredTotal) {
int sum = (maxChoosableInteger + 1) * maxChoosableInteger / 2;
if (sum < desiredTotal) return false;
if (sum == desiredTotal) {
if ((maxChoosableInteger & 1) == 1) return true;
return false;
}
if (maxChoosableInteger >= desiredTotal) return true;
boolean[] dp = new boolean[1 << maxChoosableInteger + 1];
boolean[] v = new boolean[1 << maxChoosableInteger + 1];
return dfs(maxChoosableInteger, desiredTotal, 0, dp, v);
}
public boolean dfs(int maxChoosableInteger, int desiredTotal, int cur, boolean[] dp, boolean[] v) {
if (v[cur]) return dp[cur];
v[cur] = true;
for (int i = 1; i <= maxChoosableInteger; i ++) {
int tmp = 1 << (i - 1);
if ((tmp & cur) == 0) { // 没有用到这一位
// 现在先手拿到i就赢了, 或者拿走i之后下一个拿的就会输掉,那么当前状态下是赢的
if (i >= desiredTotal || !dfs(maxChoosableInteger, desiredTotal - i, tmp | cur, dp, v)) {
dp[cur] = true;
return true;
}
}
}
dp[cur] = false;
return false;
}
}
class Solution {
public int countEval(String s, int result) {
if (s == null || s.length() == 0 || result > 1 || result < 0) return 0;
char[] chs = s.toCharArray();
int[][][] dp = new int[s.length()][s.length()][2]; // dp[i][j][k] 表示i...j计算结果是k的可能数目
for (int i = 0; i < s.length(); i += 2) {
dp[i][i][chs[i] - '0'] = 1;
}
for (int len = 2; len < s.length(); len += 2) { // 计算每一段长度为len区间的值
for (int i = 0; i + len < s.length(); i += 2) { // i...j长度为len
int j = i + len;
for (int k = i; k + 2 <= j; k += 2) {
if (chs[k + 1] == '|') {
dp[i][j][0] += dp[i][k][0] * dp[k + 2][j][0];
dp[i][j][1] += dp[i][k][1] * dp[k + 2][j][0];
dp[i][j][1] += dp[i][k][0] * dp[k + 2][j][1];
dp[i][j][1] += dp[i][k][1] * dp[k + 2][j][1];
} else if (chs[k + 1] == '&') {
dp[i][j][0] += dp[i][k][0] * dp[k + 2][j][0];
dp[i][j][0] += dp[i][k][1] * dp[k + 2][j][0];
dp[i][j][0] += dp[i][k][0] * dp[k + 2][j][1];
dp[i][j][1] += dp[i][k][1] * dp[k + 2][j][1];
} else {
dp[i][j][0] += dp[i][k][0] * dp[k + 2][j][0];
dp[i][j][0] += dp[i][k][1] * dp[k + 2][j][1];
dp[i][j][1] += dp[i][k][1] * dp[k + 2][j][0];
dp[i][j][1] += dp[i][k][0] * dp[k + 2][j][1];
}
}
}
}
return dp[0][s.length() - 1][result];
}
}
class Solution {
public int maxSubArray(int[] nums) {
int ans = Integer.MIN_VALUE;
int cur = -1;
for (int i = 0; i < nums.length; i ++) {
if (cur < 0) {
cur = nums[i];
} else {
cur += nums[i];
}
ans = Math.max(cur, ans);
}
return ans;
}
}
class Solution {
public int massage(int[] nums) {
int[][] dp = new int[nums.length + 1][2];
for (int i = 0; i < nums.length; i ++) {
dp[i + 1][0] = Math.max(dp[i][0], dp[i][1]);
dp[i + 1][1] = dp[i][0] + nums[i];
}
return Math.max(dp[nums.length][0], dp[nums.length][1]);
}
}
class Solution {
public int maxCoins(int[] nums) {
int[][] dp = new int[nums.length][nums.length];
for (int i = 0; i < nums.length; i ++) {
dp[i][i] = nums[i] * (i == 0 ? 1 : nums[i - 1]) * (i == nums.length - 1 ? 1 : nums[i + 1]);
}
for (int len = 2; len <= nums.length; len ++) {
for (int i = 0; i < nums.length; i ++) {
int j = i + len - 1;
if (j >= nums.length) break;
// k == i
int tmp1 = nums[i] * (i == 0 ? 1 : nums[i - 1]) * (j == nums.length - 1 ? 1 : nums[j + 1]);
int tmp2 = nums[j] * (i == 0 ? 1 : nums[i - 1]) * (j == nums.length - 1 ? 1 : nums[j + 1]);
dp[i][j] = Math.max(dp[i + 1][j] + tmp1, dp[i][j - 1] + tmp2);
for (int k = i + 1; k < j; k ++) {
int tmp = nums[k] * (i == 0 ? 1 : nums[i - 1]) * (j == nums.length - 1 ? 1 : nums[j + 1]);
dp[i][j] = Math.max(dp[i][j], dp[i][k - 1] + dp[k + 1][j] + tmp);
}
}
}
return dp[0][nums.length - 1];
}
}