【动态规划】算法例题

目录

一维动态规划:

 137. 爬楼梯 ①

138. 打家劫舍 ② ×

139. 单词拆分 ② ×

140. 零钱兑换 ② ×

141. 最长递增子序列 ②

多维动态规划:

142. 三角形最小路径和 ②

143. 最小路径和 ②

144. 不同路径 II ②

145. 最长回文子串 ②

146. 交错字符串 ②

147. 编辑距离 ②

148. 买卖股票的最佳时机 III ③

149. 买卖股票的最佳时机 IV ③

150. 最大正方形 ②


二十二、一维动态规划:

 137. 爬楼梯 ①

 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?


示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

方法1:(用递归会超时)

    public int climbStairs(int n) {
        int[] ints = new int[46];
        ints[1] = 1;
        ints[2] = 2;
        for (int i = 3; i <= n; i++) {
            ints[i] = ints[i - 1] + ints[i - 2];
        }
        return ints[n];
    }

详细图解参见:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

138. 打家劫舍 ② ×

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

解题思路:. - 力扣(LeetCode)

方法2:(0ms)

public int rob(int[] nums) {
    int prev = 0;
    int curr = 0;

    // 每次循环,计算“偷到当前房子为止的最大金额”
    for (int i : nums) {
        // 循环开始时,curr 表示 dp[k-1],prev 表示 dp[k-2]
        // dp[k] = max{ dp[k-1], dp[k-2] + i }
        int temp = Math.max(curr, prev + i);
        prev = curr;
        curr = temp;
        // 循环结束时,curr 表示 dp[k],prev 表示 dp[k-1]
    }

    return curr;
}

139. 单词拆分 ② ×

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。注意,你可以重复使用字典中的单词。
示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同

    // BFS
    public boolean wordBreak(String s, List<String> wordDict) {   // 2ms
        Queue<Integer> queue = new LinkedList<>();
        queue.add(0);

        int slength = s.length();
        boolean[] visited = new boolean[slength + 1];

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int start = queue.poll().intValue();
                for (String word : wordDict) {
                    int nextStart = start + word.length();
                    if (nextStart > slength || visited[nextStart]) {
                        continue;
                    }

                    if (s.indexOf(word, start) == start) {
                        if (nextStart == slength) {
                            return true;
                        }

                        queue.add(nextStart);
                        visited[nextStart] = true;
                    }
                }
            }
        }

        return false;
    }

    // DFS
    public boolean wordBreak(String s, List<String> wordDict) {  // 1ms
        boolean[] visited = new boolean[s.length() + 1];
        return dfs(s, 0, wordDict, visited);
    }
    private boolean dfs(String s, int start, List<String> wordDict, boolean[] visited) {
        for (String word : wordDict) {
            int nextStart = start + word.length();
            if (nextStart > s.length() || visited[nextStart]) {
                continue;
            }

            if (s.indexOf(word, start) == start) {
                if (nextStart == s.length() || dfs(s, nextStart, wordDict, visited)) {
                    return true;
                }
                visited[nextStart] = true;
            }
        }
        return false;
    }

    // DP
    public boolean wordBreak(String s, List<String> wordDict) { // 1ms
        int maxWordLength = 0;
        Set<String> wordSet = new HashSet<>(wordDict.size());
        for (String word : wordDict) {
            wordSet.add(word);

            if (word.length() > maxWordLength) {
                maxWordLength = word.length();
            }
        }

        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i < dp.length; i++) {
            for (int j = (i - maxWordLength < 0 ? 0 : i - maxWordLength); j < i; j++) {
                if (dp[j] && wordSet.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[dp.length - 1];
    }
//dp 8ms
public boolean wordBreak(String s, List<String> wordDict) {
	 Set<String> wordDictSet = new HashSet(wordDict);
	boolean[] dp = new boolean[s.length() + 1];
	dp[0] = true;
	for (int i = 1; i <= s.length(); i++) {
		for (int j = 0; j < i; j++) {
			if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
				dp[i] = true;
				break;
			}
		}
	}
	return dp[s.length()];
}

140. 零钱兑换 ② ×

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
 
解释:11 = 5 + 5 + 1
示例 2:

输入:coins = [2], amount = 3

输出:-1
示例 3:

输入:coins = [1], amount = 0
输出:0


提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

    解题思路:

    . - 力扣(LeetCode)

    12ms:

    class Solution {
        public int coinChange(int[] coins, int amount) {
            // 自底向上的动态规划
            if(coins.length == 0){
                return -1;
            }
    
            // memo[n]的值: 表示的凑成总金额为n所需的最少的硬币个数
            int[] memo = new int[amount+1];
            // 给memo赋初值,最多的硬币数就是全部使用面值1的硬币进行换
            // amount + 1 是不可能达到的换取数量,于是使用其进行填充
            Arrays.fill(memo,amount+1);
            memo[0] = 0;
            for(int i = 1; i <= amount;i++){
                for(int j = 0;j < coins.length;j++){
                    if(i - coins[j] >= 0){
                        // memo[i]有两种实现的方式,
                        // 一种是包含当前的coins[i],那么剩余钱就是 i-coins[i],这种操作要兑换的硬币数是 memo[i-coins[j]] + 1
                        // 另一种就是不包含,要兑换的硬币数是memo[i]
                        memo[i] = Math.min(memo[i],memo[i-coins[j]] + 1);
                    }
                }
            }
    
            return memo[amount] == (amount+1) ? -1 : memo[amount];
        }
    }
    
    作者:sugar
    链接:https://leetcode.cn/problems/coin-change/solutions/137661/javadi-gui-ji-yi-hua-sou-suo-dong-tai-gui-hua-by-s/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    18ms:

    class Solution {
        public int coinChange(int[] coins, int amount) {
            // 自底向上的动态规划
            if(coins.length == 0){
                return -1;
            }
    
            // memo[n]的值: 表示的凑成总金额为n所需的最少的硬币个数
            int[] memo = new int[amount+1];
            memo[0] = 0;
            for(int i = 1; i <= amount;i++){
                int min = Integer.MAX_VALUE;
                for(int j = 0;j < coins.length;j++){
                    if(i - coins[j] >= 0 && memo[i-coins[j]] < min){
                        min = memo[i-coins[j]] + 1;
                    }
                }
                // memo[i] = (min == Integer.MAX_VALUE ? Integer.MAX_VALUE : min);
                memo[i] = min;
            }
    
            return memo[amount] == Integer.MAX_VALUE ? -1 : memo[amount];
        }
    }
    
    作者:sugar
    链接:https://leetcode.cn/problems/coin-change/solutions/137661/javadi-gui-ji-yi-hua-sou-suo-dong-tai-gui-hua-by-s/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    36ms:

    class Solution {
        int[] memo;
        public int coinChange(int[] coins, int amount) {
            if(coins.length == 0){
                return -1;
            }
            memo = new int[amount];
    
            return findWay(coins,amount);
        }
        // memo[n] 表示钱币n可以被换取的最少的硬币数,不能换取就为-1
        // findWay函数的目的是为了找到 amount数量的零钱可以兑换的最少硬币数量,返回其值int
        public int findWay(int[] coins,int amount){
            if(amount < 0){
                return -1;
            }
            if(amount == 0){
                return 0;
            }
            // 记忆化的处理,memo[n]用赋予了值,就不用继续下面的循环
            // 直接的返回memo[n] 的最优值
            if(memo[amount-1] != 0){
                return memo[amount-1];
            }
            int min = Integer.MAX_VALUE;
            for(int i = 0;i < coins.length;i++){
                int res = findWay(coins,amount-coins[i]);
                if(res >= 0 && res < min){
                    min = res + 1; // 加1,是为了加上得到res结果的那个步骤中,兑换的一个硬币
                }
            }
            memo[amount-1] = (min == Integer.MAX_VALUE ? -1 : min);
            return memo[amount-1];
        }
    }
    
    作者:sugar
    链接:https://leetcode.cn/problems/coin-change/solutions/137661/javadi-gui-ji-yi-hua-sou-suo-dong-tai-gui-hua-by-s/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    141. 最长递增子序列 ②

     给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

    子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

    示例 1:

    输入:nums = [10,9,2,5,3,7,101,18]
    输出:4
    解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
    示例 2:

    输入:nums = [0,1,0,3,2,3]
    输出:4
    示例 3:

    输入:nums = [7,7,7,7,7,7,7]
    输出:1

    提示:

    • 1 <= nums.length <= 2500
    • -104 <= nums[i] <= 104

    进阶:

    • 你能将算法的时间复杂度降低到 O(n log(n)) 吗?

    方法1:

        public int lengthOfLIS(int[] nums) {
            int[] records = new int[nums.length];
            Arrays.fill(records, 1);
            int max = records[0];
            for (int i = 0; i < nums.length; i++) {
                for (int j = i + 1; j < nums.length; j++) {
                    if (nums[j] > nums[i]){
                        records[j] = Math.max(records[i] + 1, records[j]);
                    }
                }
                max = Math.max(records[i], max);
            }
            return max;
        }

     图解参见:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

    方法2:(0ms)

        public int lengthOfLIS(int[] nums) {
            if(nums.length == 1) return 1;
            
            int n = nums.length;
            int[] dp = new int[n];
            
            int len = 0;
            for(int i: nums){
                int index = Arrays.binarySearch(dp, 0, len, i);
                if(index < 0) index = -(index+1);
                dp[index] = i;
                if(index == len) len++;
            }
            return len;
        }

    方法3:(2ms)

        public int lengthOfLIS(int[] nums) {
            int ans = 0;
            int[] end = new int[nums.length];
            for(int num : nums){
                int l = 0, r = ans; // ans就是现在的个数
                while(l < r){
                    int mid = l + (r - l)/2;
                    if(end[mid] < num) l = mid + 1;
                    else r = mid;
                }
                end[l] = num;
                if(l == ans) ans++; // 刚好处在边界
            }
            return ans;
        }

    二十三、多维动态规划:

    142. 三角形最小路径和 ②

     给定一个三角形 triangle ,找出自顶向下的最小路径和。

    每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。

    示例 1:

    输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
    输出:11
    解释:如下面简图所示:
       2
      3 4
     6 5 7
    4 1 8 3
    自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
    示例 2:

    输入:triangle = [[-10]]
    输出:-10

    提示:

    • 1 <= triangle.length <= 200
    • triangle[0].length == 1
    • triangle[i].length == triangle[i - 1].length + 1
    • -104 <= triangle[i][j] <= 104

    进阶:

    • 你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题吗?

    方法2:动态规划(3ms)
    定义二维 dp 数组,将解法二中「自顶向下的递归」改为「自底向上的递推」。

    1、状态定义:

    2、状态转移:

    3、代码实现:

        public int minimumTotal(List<List<Integer>> triangle) {
            int n = triangle.size();
            // dp[i][j] 表示从点 (i, j) 到底边的最小路径和。
            int[][] dp = new int[n + 1][n + 1];
            // 从三角形的最后一行开始递推。
            for (int i = n - 1; i >= 0; i--) {
                for (int j = 0; j <= i; j++) {
                    dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j);
                }
            }
            return dp[0][0];
        }
    
    作者:Sweetiee 🍬
    链接:https://leetcode.cn/problems/triangle/solutions/329394/di-gui-ji-yi-hua-dp-bi-xu-miao-dong-by-sweetiee/
    

    时间复杂度:O(N^2),N 为三角形的行数。
    空间复杂度:O(N^2),N 为三角形的行数。

    方法3:空间优化

    作者:Sweetiee 🍬
    链接:https://leetcode.cn/problems/triangle/solutions/329394/di-gui-ji-yi-hua-dp-bi-xu-miao-dong-by-sweetiee/
     

        public int minimumTotal(List<List<Integer>> triangle) {
            int n = triangle.size();
            int[] dp = new int[n + 1];
            for (int i = n - 1; i >= 0; i--) {
                for (int j = 0; j <= i; j++) {
                    dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);
                }
            }
            return dp[0];
        }
    
    作者:Sweetiee 🍬
    链接:https://leetcode.cn/problems/triangle/solutions/329394/di-gui-ji-yi-hua-dp-bi-xu-miao-dong-by-sweetiee/
    

    时间复杂度:O(N^2),N 为三角形的行数。
    空间复杂度:O(N),N 为三角形的行数。

    递归见:. - 力扣(LeetCode)

    0ms答案:

        public int minimumTotal(List<List<Integer>> triangle) {
            int[] a = new int[triangle.size()];
            for (int i = 0; i < triangle.size(); ++i) minimumTotal(triangle, a, i);
            int min = Integer.MAX_VALUE;
            for (int i : a) if (i < min) min = i;
            return min;
        }
    
        private static void minimumTotal(List<List<Integer>> triangle, int[] last, int i) {
            List<Integer> line = triangle.get(i);
            if (i == 0) {
                last[0] = line.get(0);
                return;
            }
            last[i] = last[i - 1] + line.get(i);
            last[i - 1] += line.get(i - 1);
            for (int j = i - 2; j > -1; --j) {
                int sum = last[j] + line.get(j + 1);
                if (sum < last[j + 1]) last[j + 1] = sum;
                last[j] += line.get(j);
            }
        }

    1ms答案:

        public int minimumTotal(List<List<Integer>> triangle) {
            memo = new Integer[triangle.size()][triangle.size()];
            return  dfs(triangle, 0, 0);
        }
        Integer[][] memo;
        private int dfs(List<List<Integer>> triangle, int i, int j) {
            if (i == triangle.size()) {
                return 0;
            }
            if (memo[i][j] != null) {  //计算过的最小路径就无需再计算,直接返回
                return memo[i][j];
            }
            return memo[i][j] = Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j); //当前元素加下层中最短的路径和
        }

    2ms答案:

        public int minimumTotal(List<List<Integer>> triangle) {
            int[] minSum = new int[triangle.size()];
            // for(int i = 0; i < minSum.length; ++i) minSum[i] = Integer.MAX_VALUE-1444;
            minSum[0] = triangle.get(0).get(0);
            for(int i = 1; i < minSum.length; ++i){
                List<Integer> p = triangle.get(i);
                int last = minSum[0], pum = 0;
                for(int j = 0; j < i+1; ++j){
                    if(j == 0) pum = last+p.get(j);
                    else if(j == i) pum = last+p.get(j);
                    else pum = Math.min(last, minSum[j])+p.get(j);
                    last = minSum[j];
                    minSum[j] = pum;
                }
            }
            int min = Integer.MAX_VALUE;
            for(int i: minSum){
                if (i < min) min=i;
            }
            return min;
    
        }

    143. 最小路径和 ② √-

    给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

    说明:每次只能向下或者向右移动一步。

    示例 1:

    输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
    输出:7
    解释:因为路径 1→3→1→1→1 的总和最小。
    示例 2:

    输入:grid = [[1,2,3],[4,5,6]]
    输出:12

    提示:

    m == grid.length
    n == grid[i].length
    1 <= m, n <= 200
    0 <= grid[i][j] <= 200

    方法1:

      public static int minPathSum(int[][] grid) {
      	record = new int[grid.length + 1][grid[0].length + 1];
      	for (int i = 0; i < grid.length; i++) {
      		for (int j = 0; j < grid[0].length; j++) {
      			if (i == 0 && j == 0){
      				record[i + 1][j + 1] = grid[i][j];
      			} else if (i == 0) {
      				record[i + 1][j + 1] = record[i + 1][j] + grid[i][j];
      			} else if (j == 0){
      				record[i + 1][j + 1] = record[i][j + 1] + grid[i][j];
      			} else {
      				record[i + 1][j + 1] = Math.min(record[i][j + 1], record[i + 1][j]) + grid[i][j];
      			}
      		}
      	}
      	return record[grid.length][grid[0].length];
      }

      方法2:(0ms)

      class Solution {
          public int minPathSum(int[][] grid) {
              int m = grid.length;
              int n = grid[0].length;
              int dp[] = new int[n];
              dp[0] = grid[0][0];
              for (int i = 1; i < n; i++) {
                  dp[i] = grid[0][i] + dp[i - 1];
              }
              for (int i = 1; i < m; i++) {
                  a(dp, grid[i]);
              }
              return dp[n - 1];
          }
      
          public void a(int[] dp, int[] cur) {
              dp[0] += cur[0];
              for (int j = 1; j < cur.length; j++) {
                  dp[j] = Math.min(dp[j], dp[j - 1]) + cur[j];
              }
          }
      }

       1ms:

      class Solution {
          public int minPathSum(int[][] grid) {
              int m = grid.length;
              int n = grid[0].length;
              int[][] memo = new int[m][n];
              for (int[] row : memo) {
                  Arrays.fill(row, -1); // -1 表示没有计算过
              }
              return dfs(m - 1, n - 1, grid, memo);
          }
      
          private int dfs(int i, int j, int[][] grid, int[][] memo) {
              if (i < 0 || j < 0) {
                  return Integer.MAX_VALUE;
              }
              if (i == 0 && j == 0) {
                  return grid[i][j];
              }
              if (memo[i][j] != -1) { // 之前计算过
                  return memo[i][j];
              }
              return memo[i][j] = Math.min(dfs(i, j - 1, grid, memo), dfs(i - 1, j, grid, memo)) + grid[i][j];
          }
      }

      2ms:

      class Solution {
          public int minPathSum(int[][] grid) {
              int n = grid.length;
              int m = grid[0].length;
              int[] f = new int[m+1];
              Arrays.fill(f,Integer.MAX_VALUE/2);
              f[1] = 0;
              for(int i = 0 ; i < n ; i++){
                  for(int j = 0 ; j < m ; j++){
                      f[j+1] = Math.min(f[j],f[j+1]) + grid[i][j];
                  }
              }
              return f[m];
          }
      }

       3ms:

      class Solution {
          public int minPathSum(int[][] grid) {
              int m=grid.length,n=grid[0].length;
              int[][] dp=new int[m][n];
              dp[0][0]=grid[0][0];
              for(int j=1;j<n;j++){
                  dp[0][j]=dp[0][j-1]+grid[0][j];
              }
              for(int i=1;i<m;i++){
                  dp[i][0]=dp[i-1][0]+grid[i][0];
              }
              for(int i=1;i<m;i++){
                  for(int j=1;j<n;j++){
                      dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
                  }
              }
              return dp[m-1][n-1];
          }
      }

      144. 不同路径 II ② √-

      给定一个 m x n 的整数数组 grid。一个机器人初始位于 左上角(即 grid[0][0])。机器人尝试移动到 右下角(即 grid[m - 1][n - 1])。机器人每次只能向下或者向右移动一步。

      网格中的障碍物和空位置分别用 1 和 0 来表示。机器人的移动路径中不能包含 任何 有障碍物的方格。

      返回机器人能够到达右下角的不同路径数量。

      测试用例保证答案小于等于 2 * 109

      示例 1:

      输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
      输出:2
      解释:3x3 网格的正中间有一个障碍物。
      从左上角到右下角一共有 2 条不同的路径:
      1. 向右 -> 向右 -> 向下 -> 向下
      2. 向下 -> 向下 -> 向右 -> 向右

      示例 2:

      输入:obstacleGrid = [[0,1],[0,0]]
      输出:1

      提示:

      • m == obstacleGrid.length

      • n == obstacleGrid[i].length

      • 1 <= m, n <= 100

      • obstacleGrid[i][j] 为 0 或 1

      思路:每个格子的方法数=其上方和左方格子之和

      1ms:(5%)

      public static int uniquePathsWithObstacles(int[][] obstacleGrid) {
      	int[][] record = new int[obstacleGrid.length][obstacleGrid[0].length];
      	if (obstacleGrid[0][0] != 1) record[0][0] = 1;
      	for (int i = 0; i < obstacleGrid.length; i++) {
      		for (int j = 0; j < obstacleGrid[0].length; j++) {
      			if (obstacleGrid[i][j] == 1){
      				record[i][j] = Integer.MAX_VALUE;
      			}
      		}
      	}
      	for (int i = 1; i < obstacleGrid.length; i++) {
      		if (record[i][0] != Integer.MAX_VALUE && obstacleGrid[i - 1][0] != 1) record[i][0] = record[i - 1][0];
      	}
      	for (int i = 1; i < obstacleGrid[0].length; i++) {
      		if (record[0][i] != Integer.MAX_VALUE && obstacleGrid[0][i - 1] != 1) record[0][i] = record[0][i - 1];
      	}
      	for (int i = 1; i < obstacleGrid.length; i++) {
      		for (int j = 1; j < obstacleGrid[0].length; j++) {
      			if (obstacleGrid[i][j] == 1) {
      				continue;
      			}
      			if (record[i - 1][j] == Integer.MAX_VALUE && record[i][j - 1] == Integer.MAX_VALUE){
      				record[i][j] = 0;
      
      			}else if (record[i - 1][j] == Integer.MAX_VALUE){
      				record[i][j] = record[i][j - 1];
      			}else if (record[i][j - 1] == Integer.MAX_VALUE){
      				record[i][j] = record[i - 1][j];
      			} else {
      				record[i][j] = record[i - 1][j] + record[i][j - 1];
      			}
      		}
      	}
      	return record[obstacleGrid.length - 1][obstacleGrid[0].length - 1] == Integer.MAX_VALUE? 0 : record[obstacleGrid.length - 1][obstacleGrid[0].length - 1];
      
      }

      方法2:0ms

      public int uniquePathsWithObstacles(int[][] obstacleGrid) {
      	int m = obstacleGrid.length;
      	int n = obstacleGrid[0].length;
      	int[][] dp =new int[m][n];
      	for(int i=0;i<m;i++){
      		if(obstacleGrid[i][0]==1){
      			break;
      		}
      		dp[i][0]=1;
      	}
      	for(int i=0;i<n;i++){
      		if(obstacleGrid[0][i]==1){
      			break;
      		}
      		dp[0][i]=1;
      	}
      
      	for(int i=1;i<m;i++){
      		for(int j=1;j<n;j++){
      			if(obstacleGrid[i][j]==1){
      				dp[i][j]=0;
      			}else{
      				dp[i][j] = dp[i-1][j]+dp[i][j-1];
      			}
      		}
      	}
      	return dp[m-1][n-1];
      }

      145. 最长回文子串 ② √-

      给你一个字符串 s,找到 s 中最长的 回文 子串。

      示例 1:

      输入:s = "babad"
      输出:"bab"
      解释:"aba" 同样是符合题意的答案。

      示例 2:

      输入:s = "cbbd"
      输出:"bb"

      提示:

      • 1 <= s.length <= 1000

      • s 仅由数字和英文字母组成

      方法1:(1688ms,5%)

      public static String longestPalindrome(String s) {
      	int len = 0;
      	String res = "";
      	for (int i = 0; i < s.length(); i++) {
      		int r = s.length();
      		while (r > i) {
      			r = s.lastIndexOf(s.charAt(i), r - 1);
      			int maxLen = isSymmetrical(i, r, s);
      			if (maxLen > len) {
      				len = maxLen;
      				res = s.substring(i, r + 1);
      			}
      		}
      	}
      	return res;
      }
      public static int isSymmetrical(int l, int r, String s){
      	int left = l;
      	int right = r;
      	while (left < right){
      		if (s.charAt(left) == s.charAt(right)){
      			left++;
      			right--;
      		} else {
      			return 0;
      		}
      	}
      	return r - l + 1;
      }

       6ms:从字符串第二个位置开始,以该位置为中心,向左向右遍历,找最大对称字符串

      int[] result = new int[2];
      public String longestPalindrome(String s) {
      	if (s.isEmpty() || s.length() == 1) {
      		return s;
      	}
      
      	char[] ss = s.toCharArray();
      	int len = ss.length;
      	for (int i = 1; i < len; i++) {
      		helper(ss,i,i,len);
      		helper(ss,i,i-1,len);
      	}
      	return s.substring(result[0],result[1]);
      }
      public void helper(char[] ss, int i, int j, int len) {
      	while (i >= 0 && j < len){
      		if (ss[i] == ss[j]) {
      			i--;
      			j++;
      		}else {
      			break;
      		}
      	}
      	if(j-i-1 > result[1] - result[0]){
      		result[0] = i + 1;
      		result[1] = j;
      	}
      }
      

      146. 交错字符串 ②

      147. 编辑距离 ②

      148. 买卖股票的最佳时机 III ③

      149. 买卖股票的最佳时机 IV ③

      150. 最大正方形 ②

      好的,下面我会通过一个例题来讲解运筹学动态规划在机器负荷问题上的应用。 假设有两台机器A和B,需要在三个时间段内完成五个任务。每个任务需要在一台机器上完成,且每个任务的时间不同。机器A和B在不同时间段内的可用工作时间也不同,如下表所示: | 任务 | 时间段1 | 时间段2 | 时间段3 | |------|---------|---------|---------| | 1 | 2 | 3 | 1 | | 2 | 1 | 2 | 2 | | 3 | 2 | 1 | 2 | | 4 | 1 | 3 | 1 | | 5 | 3 | 2 | 1 | | 机器 | 时间段1 | 时间段2 | 时间段3 | |------|---------|---------|---------| | A | 4 | 2 | 3 | | B | 3 | 2 | 1 | 我们需要合理地安排机器的工作负荷,使得在每个时间段内每台机器的工作时间不超过其可用工作时间,并且在三个时间段内完成所有五个任务的总时间最短。 我们可以定义一个状态表示为:$dp[i][j][k]$表示在第$i$个时间段内,机器A已完成$j$个任务,机器B已完成$k$个任务的最短完成时间。 根据状态的定义,我们可以得到状态转移方程: $$ dp[i][j][k] = \min\{dp[i-1][j][k]+t_{j+k+1}^A[i], dp[i][j-1][k]+t_{j+k+1}^B[i], dp[i][j][k-1]+t_{j+k+1}^B[i]\} $$ 其中,$t_{j+k+1}^A[i]$表示在第$i$个时间段内完成第$j+k+1$个任务需要的时间,如果在机器A上完成,则为其完成时间;如果在机器B上完成,则为在机器B上完成该任务的时间加上机器A空闲的时间。同理,$t_{j+k+1}^B[i]$表示在第$i$个时间段内完成第$j+k+1$个任务需要的时间,如果在机器B上完成,则为其完成时间;如果在机器A上完成,则为在机器A上完成该任务的时间加上机器B空闲的时间。 最终的答案为:$dp[3][5][0]$,即在第三个时间段内,机器A已完成5个任务,机器B已完成0个任务的最短完成时间。 以上就是一个简单的例题,它展示了运筹学动态规划在机器负荷问题上的应用。
      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值