目录
动态规划
动态规划相关OJ题
斐波那契数列
拆分词句
三角形
不同的路径数目(一)
动态规划是分治思想的延伸,在将大问题化解为小问题的分治过程中,保存对这些小问题已经处理好的结果,并供后面处理更大规模的问题时直接使用这些结果.
动态规划具备如下三个特点:
①把原来的问题分解成了几个相似的子问题;
②所有的子问题都只需要解决一次;
③储存子问题的解.
动态规划的本质就是对问题状态的定义和状态转移方程的定义,动态规划问题一般从以下四个角度考虑:①状态定义;②状态间的转移方程定义;③状态的初始化;④返回结果
动态规划一般适用于求最大值/最小值,判断可行或是不可行,求方案个数等等
从最基础的题目开始逐渐深入了解动态规划的思想.
OJ链接: JZ10 斐波那契数列
题目描述
大家都知道斐波那契数列,现在要求输入一个正整数 n ,请你输出斐波那契数列的第 n 项。
要求:空间复杂度 O(1),时间复杂度 O(n) ,本题也有时间复杂度 O(logn)O(logn) 的解法
例如
输入:4
返回值:3
说明:
根据斐波那契数列的定义可知,fib(1)=1,fib(2)=1,fib(3)=fib(3-1)+fib(3-2)=2,fib(4)=fib(4-1)+fib(4-2)=3,所以答案为3。
dp状态分析
状态:F(n)
状态递推:F(n) = F(n-1) + F(n-2)
初始值:F(1) = F(2) = 1
返回结果:F(n)
public class Solution {
public int Fibonacci(int n) {
if (n <= 0) {
return 0;
}
int dp[] = new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
}
OJ链接:CC12 拆分词句
题目描述
给定一个字符串s和一组单词dict,判断s是否可以用空格分割成一个单词序列,使得单词序列中所有的单词都是dict中的单词(序列可以包含一个或多个单词)。
例如:
给定s=“nowcode”;
dict=["now", "code"].
返回true,因为"nowcode"可以被分割成"now code".
dp状态分析
由于java的substring方法左闭右开,因此i从1开始.
子状态:前n个字符是否可以被成功拆分
状态定义:F(i),前i个字符是否可以被拆分
状态方程:F(i) = ( i < j && F(j) && dict.contains(s.substring(j, i)) )
初始值:初始值F(0)设为true.
返回结果:F(n)
import java.util.*;
public class Solution {
public boolean wordBreak(String s, Set dict) {
if (s.length() == 0) {
return false;
}
if (dict.isEmpty()) {
return false;
}
//定义状态数据
boolean[] dp = new boolean[s.length() + 1];
//默认初始值为true
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = i - 1; j >= 0; j--) {
if (dp[j] && dict.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
OJ链接:CC31 三角形
题目描述
给出一个三角形,计算从三角形顶部到底部的最小路径和,每一步都可以移动到下面一行相邻的数字,
例如,给出的三角形如下:
[[20],[30,40],[60,50,70],[40,10,80,30]]最小的从顶部到底部的路径和是20 + 30 + 50 + 10 = 110。
dp状态分析
状态定义:F(i, j):从(0, 0)到(i, j)的最短路径和
状态方程:F(i, j) = min( F(i - 1, j - 1),F(i - 1, j) ) + triangle[i][j]
初始值:F(0, 0) = triangle[0][0]
返回结果:min(F(n - 1, i))
import java.util.*;
public class Solution {
public int minimumTotal(ArrayList> triangle) {
if (triangle.isEmpty()) {
return 0;
}
List> dp = new ArrayList<>();
for (int i = 0; i < triangle.size(); i++) {
dp.add(new ArrayList<>());
}
//定义初始状态
dp.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 = dp.get(i - 1).get(0);
} else if (j == i) { //处理右边界
curSum = dp.get(i - 1).get(j - 1);
} else {
curSum = Math.min(dp.get(i-1).get(j), dp.get(i-1).get(j-1));
}
dp.get(i).add(curSum + triangle.get(i).get(j));
}
}
int size = triangle.size();
int ret = dp.get(size - 1).get(0);
for (int i = 1; i < size; i++) {
if (ret > dp.get(size - 1).get(i)) {
ret = dp.get(size - 1).get(i);
}
}
return ret;
}
}
OJ链接:不同的路径数目(一)
题目描述
示例
输入:2,1
返回:1
输入:2,2
返回:2
dp状态分析
状态:F(i, j):从(0,0)到(i,j)的路径数目
状态方程:F(i,j) = F(i,j-1) + F(i-1,j)
初始值:F(0,i) = 1,F(i,0) = 1
返回值:F(m - 1,n - 1)
import java.util.*;
public class Solution {
public int uniquePaths (int m, int n) {
// write code here
if (m < 1 || n < 1) {
return 0;
}
int[][] dp = new int[m][n];
for (int i = 0; i < m; i++) {
dp[i][0] = 1;
}
for (int i = 1; i < n; i++) {
dp[0][i] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = dp[i][j-1] + dp[i-1][j];
}
}
return dp[m-1][n-1];
}
}