<DP> (高频)139 375 374 (DP hard)312

139. Word Break

返回结果较为简单可用dp, 复杂用dfs

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        
        dp[0] = true;
        //枚举所有substring
        for(int i = 1; i <= s.length(); i++){
            for(int j = 0; j < i; j++){
                if(dp[j] && wordDict.contains(s.substring(j, i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}

 375. Guess Number Higher or Lower II

guarantee a win。保证最小的花费能猜到即可

建立一个二维的 dp 数组,其中 dp[i][j] 表示从数字i到j之间猜中任意一个数字最少需要花费的钱数,那么需要遍历每一段区间 [j, i],维护一个全局最小值 global_min 变量,然后遍历该区间中的每一个数字,计算局部最大值 local_max = k + max(dp[j][k - 1], dp[k + 1][i]),这个正好是将该区间在每一个位置都分为两段,然后取当前位置的花费加上左右两段中较大的花费之和为局部最大值,为啥要取两者之间的较大值呢,因为要 cover 所有的情况,就得取最坏的情况。然后更新全局最小值,最后在更新 dp[j][i] 的时候看j和i是否是相邻的,相邻的话赋为j,否则赋为 global_min。这里为啥又要取较小值呢,因为 dp 数组是求的 [j, i] 范围中的最低 cost,比如只有两个数字1和2,那么肯定是猜1的 cost 低

class Solution {
    public int getMoneyAmount(int n) {
        int[][] dp = new int[n + 1][n + 1];
        for(int i = 2; i <= n; i++){
            for(int j = i - 1; j > 0; j--){
                int globalMin = Integer.MAX_VALUE;
                for(int k = j + 1; k < i; k++){
                    int localMax = k + Math.max(dp[j][k - 1], dp[k + 1][i]);
                    globalMin = Math.min(globalMin, localMax);
                }
                dp[j][i] = j + 1 == i ? j : globalMin;
            }
        }
        return dp[1][n];
    }
}

374. Guess Number Higher or Lower

二分查找

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        if(guess(n) == 0) return n;
        int left = 1, right = n;
        
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(guess(mid) == -1){
                right = mid - 1;              
            }else if(guess(mid) == 1){
               left = mid + 1; 
            }else
                return mid;
        }
        return left;
    }
}

 312. Burst Balloons

dp[i][j] 表示打爆区间 (i, j) 中的所有气球能得到的最多金币。

第1层循环,dp[][]长度的大小k,第一轮为1个数,第二轮为2个数。

第2层循环,left的开始起点,都从0开始。 right为窗口的右边界

第3层循环, i表示打爆当前的气球,依次扫描窗口内的dp, 取最大值。

  假如第i个气球最后被打爆,那么此时区间 [left, right] 被分成了三部分,[left, i],[i],和 [i, right],只要之前更新过了 [left, i] 和[i, right]这两个子区间的 dp 值,可以直接用。

  dp[left][ right] 的意义是什么呢,相当于区间 [left, right] 中除了第i个气球,其他的已经爆了,那么周围的气球只能是第 left个,和第 right 个了

class Solution {
    public int maxCoins(int[] nums) {
        int[] iNums = new int[nums.length + 2];
        int n = 1;
        for(int x : nums) if(x > 0) iNums[n++] = x;
        iNums[0] = iNums[n++] = 1;
        
        int[][] dp = new int[n][n];
        for(int k = 2; k < n; k++){
            for (int left = 0; left < n - k; left++){
                int right = left + k;
                for(int i = left + 1; i < right; i++){
                    dp[left][right] = Math.max(dp[left][right],
                                               iNums[left]* iNums[i] * iNums[right]+ dp[left][i] + dp[i][right]);
                }
            }
        }
        return dp[0][n - 1];
    }
}
原文地址:https://www.cnblogs.com/Afei-1123/p/12010273.html