力扣刷题5-动态规划

63 阅读4分钟
237.删除链表中节点(中等)

题目描述:有一个单链表,给定节点node,需要删除这个node节点
题解:删除节点node必须找到前置节点,根据题意无法获取head节点,即不能获得前置节点,换个思路删除节点,也可以通过覆盖实现。这个题目最大挑战是能理解题意。

ListNode p = node;
ListNode q = p.next;
while (q.next != null) {
    p.val = q.val;
    p = q;
    q = q.next;
}
p.val = q.val;
p.next = null;
53.最大子数组和

题目描述:整数数组 nums ,请你找出一个具有最大和的连续子数组

思路:动态规划法。 状态转移: f(i)=max{f(i−1)+nums[i],nums[i]}

 public int maxSubArray(int[] nums) {
    int pre = 0, maxAns = nums[0];
    for (int x : nums) {
        pre = Math.max(pre + x, x);
        maxAns = Math.max(maxAns, pre);
    }
    return maxAns;
}
152 乘积最大子数组

题目描述:整数数组 nums ,请你找出数组中乘积最大的非空连续子数组,返回乘积

题解:暴力解法O(N^3)。 优化解法:使用s[i] 表示从0->i,且固定第i个元素结尾的最大连续子数组,s[i]求解需要O[N^2]

// s[i] 表示第i元素结尾的最大乘积
int[] s = new int[n];
for (int j = n - 1; j >= 0; j--) {
    int tmp = nums[j];
    int max = tmp;
    for (int i = j - 1; i >= 0; i--) {
        tmp *= nums[i];
        max = Math.max(max, tmp);
    }
    s[j] = max;
}

再优化解法: 动态规划,状态转移f[i], g[i] f[i] 表示0->i,以x[i]结尾的最大乘积 g[i] 表示0->i,以x[i]结尾的最小乘积

for (int i = 1; i < n; i++) {
    // dp
    f[i] = Math.max(nums[i] * f[i - 1], nums[i] * g[i - 1]);
    f[i] = Math.max(nums[i], f[i]);
    g[i] = Math.min(nums[i] * f[i - 1], nums[i] * g[i - 1]);
    g[i] = Math.min(nums[i], g[i]);
    res = Math.max(res, f[i]);
}
1.3 完全平方数 LC279

题目描述:给一个整数n,返回和为n的完全平方数最少数量

题解:动态规划,难点是状态转移

/**
* 动态规划解法
* f[i] 表示最少需要平方数,其和为i
* f[i] = 1 + min(f[i- j^2])
*/
public int numSquares(int n) {
    int[] f = new int[n+1];
    f[1] = 1;
    for (int i = 2; i <= n; i++) {
        int min = Integer.MAX_VALUE;
        for (int j = 1; j <= Math.sqrt(i); j++) {
            min = Math.min(min, f[i - j * j]);
        }
        f[i] = 1 + min;
//      System.out.println("index: " + i + " f[i]: " + f[i]);
    }
    return f[n];
}
64.最小路径和

题目:非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小 每次只能向下或者向右移动一步

思路:动态规划。dp[i][j]表示从start 到[i,j]位置的最小路径

dp[i][j] = Math.min(dp[i - 1][j], dp[i][j-1]) + grid[i][j]; dp初始值状态:两个边界。

public int minPathSum(int[][] grid) {
    int m = grid.length;
    int n = grid[0].length;
    int[][] dp = new int[m][n];
    dp[0][0] = grid[0][0];
    for (int i = 1; i < m; i++) {
        dp[i][0] = dp[i-1][0] + grid[i][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++) {
        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];
}
1143.最长公共子序列

题目:两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度

思路:

dp[i][j] 表示序列text1[0..i] 和 text2[0..j]的最长公共子序列长度 状态转移:

if (t1[i] == t2[j])
  dp[i][j] = dp[i-1][j-1] + 1
else
  dp[i][j] = Max(dp[i-1][j], dp[i][j-1])

初始状态:

for (int i = 0; i < m; i++) {
    dp[i][0] = 0;
}
for (int j = 0; j < n; j++) {
    dp[0][j] = 0;
}
583.字符串删除操作

题目:给定两个单词 word1 和 word2 ,返回使得 word1 和 word2 相同所需的最小步数

思路:删除最小步数,即找到最长公共子序列,剩下字符都需要删除

res = m - lcs + n - lcs

787. K 站中转内最便宜的航班

思路:

核心:递归状态转移

final int INF = 10000 * 101 + 1;
// f[t][i]表示从src节点到i节点,通过t次航班,最小费用
for (int i = 0; i < k + 2; ++i) {
    Arrays.fill(f[i], INF);
}
for (int t = 1; t <= k + 1; ++t) {
    for (int[] flight : flights) {
        int j = flight[0], i = flight[1], cost = flight[2];
        f[t][i] = Math.min(f[t][i], f[t - 1][j] + cost);
    }
}