定义:动态规划是分治思想的延伸,通俗一点来说就是大事化小,小事化无的艺术。在将大问题化解为小问题的分治过程中,保存对这些小问题已经处理好的结果,并供后面处理更大规模的问题时直接使用这些结果。
动态规划具备了以下三个特点
- 把原来的问题分解成了几个相似的子问题。
- 所有的子问题都只需要解决一次。
- 储存子问题的解。
动态规划的本质,是对问题状态的定义和状态转移方程的定义(状态以及状态之间的递推关系)
动态规划问题一般从以下四个角度考虑:
- 状态定义
- 状态间的转移方程定义
- 状态的初始化
- 返回结果
适用场景:最大值/最小值, 可不可行, 是不是,方案个数
第1题 Fibonacci斐波那契
答案一:使用递归(通过不了,因为这道题考的就不是递归,递归复杂度高,所以要用动态规划思想来做)
class Solution {
public int fib(int n) {
if (n == 0) {
return 0;
}
if (n == 1) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
}
答案二:
class Solution {
public final int MOD = 1000000007;
public int fib(int n) {
int[] arr = new int[n + 1];
if (n < 2) {
arr[n] = n;
} else {
arr[0] = 0;
arr[1] = 1;
}
for (int i = 2; i <= n; i++) {
arr[i] = (arr[i - 1] + arr[i - 2]) % MOD;
}
return arr[n];
}
}
答案三:对答案二做空间上的优化
class Solution {
public final int MOD = 1000000007;
public int fib(int n) {
if (n < 2) {
return n;
}
int f = 0;
int f1 = 0;
int f2 = 1;
for (int i = 2; i <= n; i++) {
f = (f1 + f2) % MOD;
f1 = f2;
f2 = f;
}
return f;
}
}
第2题 字符串分割(Word Break)字符串分割
class Solution {
public boolean wordBreak(String s, Set<String> dict) {
boolean[] canBreak = new boolean[s.length() + 1];
canBreak[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (canBreak[j] && dict.contains(s.substring(j, i))) {
canBreak[i] = true;
break;
}
}
}
return canBreak[s.length()];
}
}
第3题 三角矩阵(Triangle)三角矩阵
答案一:自顶向下
class Solution {
public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
if (triangle.isEmpty()) {
return 0;
}
List<List<Integer>> minPathSum = new ArrayList<>();
for (int i = 0; i < triangle.size(); i++) {
minPathSum.add(new ArrayList<>());
}
minPathSum.get(0).add(triangle.get(0).get(0));
for (int i = 1; i < triangle.size(); i++) {
int curSum = 0;
for (int j = 0; j <= i; j++) {
if (j == 0) {
curSum = minPathSum.get(i - 1).get(0);
} else if (j == i) {
curSum = minPathSum.get(i - 1).get(j - 1);
} else {
curSum = Math.min(minPathSum.get(i - 1).get(j), minPathSum.get(i - 1).get(j - 1));
}
minPathSum.get(i).add(triangle.get(i).get(j) + curSum);
}
}
int size = triangle.size();
int allMin = minPathSum.get(size - 1).get(0);
for (int i = 1; i < size; i++) {
allMin = Math.min(allMin, minPathSum.get(size - 1).get(i));
}
return allMin;
}
}
答案二:自底向上
class Solution {
public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
if (triangle.isEmpty()) {
return 0;
}
ArrayList<ArrayList<Integer>> minPathSum = new ArrayList<>(triangle);
int row = minPathSum.size();
for (int i = row - 2; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
int curSum = Math.min(triangle.get(i+1).get(j),triangle.get(i+1).get(j+1)) + triangle.get(i).get(j);
minPathSum.get(i).set(j,curSum);
}
}
return minPathSum.get(0).get(0);
}
}
第4题 路径总数(Unique Paths)路径总数
答案:
class Solution {
public int uniquePaths(int m, int n) {
List<List<Integer>> pathNum = new ArrayList<>();
for (int i = 0; i < m; i++) {
pathNum.add(new ArrayList<>());
pathNum.get(i).add(1);
}
for (int i = 1; i < n; i++) {
pathNum.get(0).add(1);
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
pathNum.get(i).add(pathNum.get(i).get(j - 1) + pathNum.get(i - 1).get(j));
}
}
return pathNum.get(m - 1).get(n - 1);
}
}
第5题 最小路径和(Minimum Path Sum)最小路径和
class Solution {
public int minPathSum(int[][] grid) {
int row = grid.length;
int col = grid[0].length;
if (grid == null) {
return 0;
}
for (int i = 1; i < row; i++) {
grid[i][0] = grid[i - 1][0] + grid[i][0];
}
for (int i = 1; i < col; i++) {
grid[0][i] = grid[0][i - 1] + grid[0][i];
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
}
}
return grid[row - 1][col - 1];
}
}
第6题 背包问题背包问题
答案一:
class Solution {
public int backPackII(int m, int[] A, int[] V) {
int num = A.length;
if (m == 0 || num == 0) {
return 0;
}
int[][] maxValue = new int[num + 1][m + 1];
for (int i = 0; i <= num; i++) {
maxValue[i][0] = 0;
}
for (int i = 1; i <= m; i++) {
maxValue[0][i] = 0;
}
for (int i = 1; i <= num; i++) {
for (int j = 1; j <= m; j++) {
if (A[i - 1] > j) {
maxValue[i][j] = maxValue[i - 1][j];
} else {
int newValue = maxValue[i - 1][j - A[i - 1]] + V[i - 1];
maxValue[i][j] = Math.max(newValue, maxValue[i - 1][j]);
}
}
}
return maxValue[num][m];
}
}
答案二:(优化算法)
class Solution {
public int backPackII(int m, int[] A, int[] V) {
int num = A.length;
if (m == 0 || num == 0) {
return 0;
}
int[] maxValue = new int[m + 1];
for (int i = 0; i <= m; i++) {
maxValue[i] = 0;
}
for (int i = 1; i <= num; i++) {
for (int j = m; j > 0; j--) {
if (A[i - 1] <= j) {
int newValue = maxValue[j - A[i - 1]] + V[i - 1];
maxValue[j] = Math.max(newValue, maxValue[j]);
}
}
}
return maxValue[m];
}
}
第7题 回文串分割(Palindrome Partitioning)回文串分割
答案:好难,没理解,痛苦
class Solution {
public int minCut(String s) {
int len = s.length();
if (len == 0) {
return 0;
}
int[] minCut = new int[len + 1];
for (int i = 0; i <= len; i++) {
minCut[i] = i - 1;
}
for (int i = 1; i <= len; i++) {
for (int j = 0; j < i; j++) {
if (isPal(s, j, i - 1)) {
minCut[i] = Math.min(minCut[i], minCut[j] + 1);
}
}
}
return minCut[len];
}
public boolean isPal(String s, int start, int end) {
while (start < end) {
if (s.charAt(start) != s.charAt(end)) {
return false;
}
start++;
end--;
}
return true;
}
}