总结
动态规划特点,从已知数据状态,推断出下一个数据状态。例如:{A1->A2}; {A1, A2->A3}; {A1,A2,A3->A4};……; {A1,A2,...,Ai}->Ai+1.
-
最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
-
无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
-
有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
常见问题
- 斐波那契数列
当前数据由前两种状态共同绝对,符合动态规范算法
int solutionFibonacci(int n) {
if (n <= 1) {
return n;
} else {
int[] result = new int[n+1];
result[0] = 0;
result[1] = 1;
for (int i = 2; i <= n; i++) {
result[i] = result[i-1] + result[i-2];
}
return result[n];
}
}
- 跳台阶问题:每次只能跳一个或者两个台阶,跳到n层台阶上有几种方法
假设当前为第N台阶,那么上一次状态的位置只能是N-1或者N-2,只有这两种状态,符合动态规范算法
public int JumpFloor(int target) {
if (target <= 1) {
return target;
}
int[] a = new int[target + 1];
a[1] = 1;
a[2] = 2;
for (int i = 3; i <= target; i++) {
a[i] = a[i - 1] + a[i - 2];
}
return a[target];
}
同理,那如果:可以跳一个或者两个台阶或者三个台阶
那么F(n)=F(n-1) + F(n-2) + F(n-3),其中n>3
这类问题与找零钱一致
,比如100块,可以换10块,或者20块的组合有多少种,类似于一次跳一个或者两个台阶,当前台阶为10
填充长方体问题:将一个2*1的长方体填充到2*n的长方体中,有多少种方法
(同上)
- 数组最大不连续递增子序列
这个是由于当前的数字Xn是否比前面的数字Xm大,若Xn>Xm,与Xn<Xm两种情况,符合动态规范
public static int maxContinueSequence(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result[i] = 1;
}
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < i; j++) {
if (arr[j] < arr[i] && (result[j] + 1) > result[i]) {
result[i] = result[j] + 1;
}
}
}
int max = 0;
for (int i = 1; i < result.length; i++) {
if (result[max] < result[i]) {
max = i;
}
}
return result[max];
}
- 数组最大连续子序列和
当前数字之和Xm是不是最大,取决于要不要加入前一个数字的状态和,两种状态,符合动态规范
public static int maxContinueSequenceSum(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if ((max + arr[i]) > arr[i]) {
max = max + arr[i];
} else {
max = arr[i];
}
}
return max;
}
- 数字塔从上到下所有路径中和最大的路径
只有两种走法,红色的只能由蓝色两个而来,符合动态规划
public static int maxPathSum(int[][] dataArray) {
int size = dataArray.length;
int[][] result = new int[size+1][size+1];
for (int i = 0; i < size; i++) {
result[i][0] = 0;
result[0][i] = 0;
}
int max = 0;
for (int i = 1; i <= size; i++) {
for (int j = 1; j <= i; j++) {
if (result[i-1][j-1] > result[i-1][j]) {
result[i][j] = result[i-1][j-1] + dataArray[i-1][j-1];
} else {
result[i][j] = result[i-1][j] + dataArray[i-1][j-1];
}
if (max < result[i][j]) {
max = result[i][j];
}
}
}
return max;
}
- 两个字符串最大公共子序列
只有相等或者不等的情况,不相等,前移一个位置,重复这个操作,符合动态规划
public static int maxContinueEqualArrayLength(String str1, String str2) {
int m = str1.length();
int n = str2.length();
int[][] dp = new int[m+1][n+1];
for (int i = 0; i < m; i++) {
dp[i][0] = 0;
}
for (int i = 0; i < n; i++) {
dp[0][i] = 0;
}
int max = 0;
for (int i = 1; i <= m; i++) {
char c1 = str1.charAt(i-1);
for (int j = 1; j <= n; j++) {
char c2 = str2.charAt(j-1);
if (c1 == c2) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = dp[i-1][j] > dp[i][j-1] ? dp[i-1][j] : dp[i][j-1];
}
if (dp[i][j] > max) {
max = dp[i][j];
}
}
}
return max;
}
- 背包问题
// i是第几件物品,j表示物品的重量,dp[i][j]表示价值
// DP: dp[i][j] 表示j的背包中装进前i件物品所得到的最大价值。
// 递推关系:dp[i][j] = max {dp[i - 1][j], dp[i - 1][j - weight[i]] + price[i]};