1. 动态规划(Dynamic Programming,DP)
动态规划通常用来求解复杂问题的某个最优解,与分治法相似,区别在于
- 分治法应用于子问题互不相交的情况,即递归的每一步都生成全新的子问题
- 动态规划应用于重叠子问题的情况,即递归反复求解相同的子问题
使用条件
- 最优子结构:问题的最优解由子问题的最优解组合而成,子问题可以独立求解
- 重叠子问题:不同的子问题可能有公共的子子问题,导致反复求解相同的问题
实现方法
- 带备忘的自顶向下法(top-down with memoization)
用数组或哈希表保存每个子问题的解,求解子问题时先看是否已经保存过 - 自底向上法(bottom-up)
对于任何子问题,其子问题均已求解完成,才会求解它
两种方法核心都在于保持递归的流程同时,维护了一个表记录子问题的解
设计步骤
- 考察问题是否具有最优子结构,刻画最优解的结构
- 递归的定义,最优解的值
- 计算最优解的值
- 通常使用 bottom-up 法
1.1 最长公共子序列(Longest Common Subsequence,LCS)
LeetCode 1143题:
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length();
int n = text2.length();
int[][] c = new int[m+1][n+1];
int i,j;
for (i = 0; i <= m; i++)
c[i][0] = 0;
for (j = 0; j <= n; j++)
c[0][j] = 0;
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (text1.charAt(i-1) == text2.charAt(j-1)) {
c[i][j] = c[i-1][j-1] + 1;
} else {
c[i][j] = Math.max(c[i-1][j], c[i][j-1]);
}
}
}
return c[m][n];
}
}
几乎所有的动态规划解决方案,首先会创建一个一维或多维数组 DP来保存中间子解的值,以及通常数组最后一个值代表最终解
- 当存在一个字符串为空串时,LCS不存在,长度等于0
- 用索引0表示了空串,因此DP表初始化为 dp[str1.length()+1][str2.length()+1]
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201105173521570.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvZGZpc2hYWQ==,size_16,color_FFFFFF,t_70#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201105172608961.png#pic_center)
2. 贪心算法
在当前步做出看起来最佳的选择,不依赖于子问题的解,最终可能获得全局最优解
动态规划每步都要进行一次选择,通常依赖于当前步子问题的解
使用条件
- 贪心选择性质:不需要考虑子问题的解,就可做出当前问题看起来最优的选择
- 最优子结构
设计步骤
- 做出一次选择后,只剩下一个子问题需要求解
- 贪心选择后,原问题总是存在最优解
- 剩余子问题最优解与贪心选择组合即可获得原问题的最优解
- 通常使用 top-down 法
2.1 最小生成树
图的生成树:一个无向图的极小连通子图,它有n个顶点,n-1条边
最小生成树:以最小代价构成的极小连通子图
2.1.1 Kruskal 算法
每次都选择权重最小的边(贪心选择),排除掉会构成环的边,最终构成最小生成树
代码核心
- 并查集:Find 查询两个元素是否在同一个集合中,Union 把两个不相交的集合合并为一个集合
- 权重排序(Java使用Collections工具类,对ArrayList<Edge>中的edge按权重排序)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201025153034244.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvZGZpc2hYWQ==,size_16,color_FFFFFF,t_70#pic_center)
LeetCode 1584题:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201025154651659.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NvZGZpc2hYWQ==,size_16,color_FFFFFF,t_70#pic_center)
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class MinCostConnectPoints {
int[] parent;
public int findRoot(int x){
return parent[x] == x ? x : findRoot(parent[x]);
}
public int mccp(int[][] points) {
int v = points.length;
int w = 0;
int result = 0;
parent = new int[v];
for (int k = 0; k < v; k++) {
parent[k] = k;
}
ArrayList<Edge> edges = new ArrayList();
for (int i = 1; i < v; i++) {
for (int j = 0; j < i; j++) {
w = Math.abs(points[i][0] - points[j][0]) +
Math.abs(points[i][1] - points[j][1]);
edges.add(new Edge(i, j, w));
}
}
Collections.sort(edges, new Comparator<Edge>() {
@Override
public int compare(Edge o1, Edge o2) {
return o1.compareTo(o2);
}
});
for (int m = 0; m < edges.size(); m++) {
if (findRoot(edges.get(m).begin) != findRoot(edges.get(m).end)) {
result += edges.get(m).weight;
parent[findRoot(edges.get(m).begin)] = findRoot(edges.get(m).end);
}
}
return result;
}
}
class Edge implements Comparable<Edge> {
int begin;
int end;
int weight;
public int getBegin() {
return begin;
}
public void setBegin(int begin) {
this.begin = begin;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Edge(int begin, int end, int weight) {
this.begin = begin;
this.end = end;
this.weight = weight;
}
public Edge() {
}
@Override
public int compareTo(Edge o) {
return Integer.compare(this.weight, o.weight);
}
@Override
public String toString() {
return "Edge{" +
"begin=" + begin +
", end=" + end +
", weight=" + weight +
'}';
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)