LeetCode刷题记190-Dynamic Programming(动态规划)

354. 俄罗斯套娃信封问题

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];
    }
}

464. 我能赢吗

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;
    }
}

面试题 08.14. 布尔运算

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];
    }
}

面试题 16.17. 连续数列

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;
    }
}

面试题 17.16. 按摩师

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]);
    }
}

312. 戳气球

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];
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值