动态规划(英语:Dynamic programming,简称 DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再根据子问题的解以得出原问题的解。动态规划往往用于优化递归问题,例如斐波那契数列,如果运用递归的方式来求解会重复计算很多相同的子问题,利用动态规划的思想可以减少计算量。
通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,具有天然剪枝的功能,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。
链接:https://leetcode-cn.com/tag/dynamic-programming/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
学习动态规划,这是一种解决棘手问题的方法,它将问题分解成小问题,并先着手解决这些小问题。
每个动态规划算法都从一个网格开始
动态规划可以帮助在给定约束条件下找到最优解
一个问题如果要用动态规划解的话,需要考虑下面的几个点:
背包问题
旅游行程最优化
最长公共子串
疑问:如果答案不在最后一个格子的话,该怎么取值?
最长公共子序列
5.最长回文子串
//java 通过了
class Solution {
public String longestPalindrome(String s) {
String res = "";
int n = s.length();
boolean[][] dp = new boolean[n][n];
for(int i = n; i >= 0; i--){
for(int j = i; j < n; j++){
if(j == i || j == i+1){
dp[i][j] = (s.charAt(i) == s.charAt(j));
}else{
dp[i][j] = (dp[i+1][j-1] && s.charAt(i) == s.charAt(j));
}
if(dp[i][j] && (j-i+1 > res.length())){
res = s.substring(i, j+1);
}
}
}
return res;
}
}
/*
输入:"babad"
i\j 0 1 2 3 4
0 1 0 1 0 0
1 0 1 0 1 0
2 0 0 1 0 0
3 0 0 0 1 0
4 0 0 0 0 1
*/
#和上面的java思路一样 但是一直超出时间限制
class Solution:
def longestPalindrome(self, s: str) -> str:
res = "" #最长回文子串
a=[[False for _ in range(len(s))]for _ in range(len(s))] #生成二维数组并初始化
for i in range(len(s)-1, -1, -1): #长度为5的话,输出:4,3,2,1,0
for j in range(i, len(s)):
#print(i, j)
if(j == i or j == i+1):
a[i][j] = (s[i] == s[j])
else:
a[i][j] = (a[i+1][j-1] == 1 and s[i] == s[j])
if(a[i][j] and (j-i+1 > len(res))):
res = s[i:j+1]
return res
动态规划 空间优化
//java
class Solution {
public String longestPalindrome(String s) {
String res = "";
int n = s.length();
boolean[] dp = new boolean[n];
for(int i = n-1; i >= 0; i--){
for(int j = n-1; j >= i; j--){
if(j == i || j == i+1){
dp[j] = (s.charAt(i) == s.charAt(j));
}else{
dp[j] = (dp[j-1] && s.charAt(i) == s.charAt(j));
}
if(dp[j] && (j-i+1 > res.length())){
res = s.substring(i, j+1);
}
}
}
return res;
}
}
62.不同路径
//java anan 2020.3.2
class Solution {
public int uniquePaths(int m, int n) {
int dp[][] = new int[n][m];
dp[0][0] = 1;
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(i > 0 || j > 0){ //设置条件是为了除去初值
dp[i][j] = (i-1>=0 ? dp[i-1][j] : 0) + (j-1>=0 ? dp[i][j-1] : 0);
}
}
}
return dp[n-1][m-1];
}
}
63.不同路径 II
//java anan 2020.3.2
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int n = obstacleGrid.length;
int m = obstacleGrid[0].length;
if(obstacleGrid[n-1][m-1] == 1){ //[[0,1]]
return 0;
}
int dp[][] = new int[n][m];
dp[0][0] = (obstacleGrid[0][0] == 0) ? 1:0; //[[0]] [[1]]
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(i > 0 || j > 0){ //设置条件是为了除去初值
dp[i][j] = (i-1>=0 && obstacleGrid[i-1][j]==0 ? dp[i-1][j] : 0) + (j-1>=0 && obstacleGrid[i][j-1]==0? dp[i][j-1] : 0); //加了一些判定条件
}
}
}
return dp[n-1][m-1];
}
}
64.最小路径和
//java anan 2020.3.2
class Solution {
public int minPathSum(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[][] dp = new int[n][m]; //dp中存的是到当前位置的最短路径
dp[0][0] = grid[0][0];
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(i > 0 || j > 0){
dp[i][j] = Math.min((i-1>=0? dp[i-1][j] : Integer.MAX_VALUE), (j-1>=0? dp[i][j-1] : Integer.MAX_VALUE)) + grid[i][j];
}
}
}
return dp[n-1][m-1];
}
}
空间优化,用一维数组
//java anan 2020.3.2
class Solution {
public int minPathSum(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[] dp = new int[m]; //dp中存的是到当前位置的最短路径
dp[0] = grid[0][0];
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
if(i > 0 || j > 0){
dp[j] = Math.min((i-1>=0? dp[j] : Integer.MAX_VALUE), (j-1>=0? dp[j-1] : Integer.MAX_VALUE)) + grid[i][j];
}
}
}
return dp[m-1];
}
}
53.最大子序和
//java
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
int max = nums[0];
dp[0] = nums[0];
for(int i = 1; i < n; i++){
if(dp[i-1] < 0){
dp[i] = nums[i];
}else{
dp[i] = dp[i-1]+nums[i];
}
max = Math.max(max, dp[i]);
}
return max;
}
}
70.爬楼梯
//C
int climbStairs(int n){
/*
f(1) = 1
f(2) = 2
f(3) = f(1)+ f(2)
f(4) = f(2) + f(3)
f(n) = f(n-2) + f(n-1)
*/
int num[n+1];
for(int i = 0; i < n+1; i++){
if(i <= 2){
num[i] = i;
}else{
num[i] = num[i-1]+num[i-2];
}
printf("%d ", num[i]);
}
return num[n];
}
//C 代码优化
int climbStairs(int n){
int num[n+1];
for(int i = 0; i < n+1; i++){
num[i] = (i <= 2) ? i : num[i-1]+num[i-2];
}
return num[n];
}
139.单词拆分
#python anan 根据题解思想写的
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
n = len(s)
dp = [False for _ in range(n+1)] #dp[i]表示s[0:i]可以由字典中的词构成
dp[0] = True
for i in range(n):
for j in range(i+1, n+1):
if(s[i:j] in wordDict and (dp[i])):
dp[j] = True
#print(i, j, s[i:j], dp)
return dp[-1]
//java
public class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> wordDictSet=new HashSet(wordDict);
boolean[] dp = new boolean[s.length() + 1];
dp[0] = true;
for (int i = 1; i <= s.length(); i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && wordDictSet.contains(s.substring(j, i))) {
dp[i] = true;
break;
}
}
}
return dp[s.length()];
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/word-break/solution/dan-ci-chai-fen-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
91.解码方法
#python anan 2020.3.2
class Solution:
def numDecodings(self, s: str) -> int:
n = len(s)
dp = [1 for _ in range(n+1)]
for i in range(1,n+1):
dp[i] = (dp[i-1] if (i >= 1 and 0 < int(s[i-1]) <= 26) else 0) + (dp[i-2] if (i >= 2 and 0 < int(s[i-2:i]) <= 26 and int(s[i-2])!=0) else 0)
# 加上int(s[i-2])!=0 是因为:"01"
#print(dp)
dp[0] = 0 # ""空串时结果应该是0,上面设置成1是为了好迭代
return dp[-1]
96.不同的二叉搜索树
#python
class Solution {
public int numTrees(int n) {
int[] dp = new int[n+1];
dp[0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= i; j++){
dp[i] += dp[j-1]*dp[i-j];
}
}
return dp[n];
}
}
322.零钱兑换
//java 2020.3.9 自己结合官解思路自己写的
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount == 0){ //特殊例子: [1] 0
return 0;
}
int[] dp = new int[amount+1];
int min;
Arrays.sort(coins); //在原数组上进行排序
Arrays.fill(dp, amount+1);
dp[0] = 0;
for(int i = 1; i < amount+1; i++){
for(int j = 0; j < coins.length; j++){
if(i-coins[j] >= 0){
dp[i] = Math.min(dp[i], dp[i-coins[j]]+1);
}
}
}
// for(int d: dp){
// System.out.print(d + " ");
// }
return dp[amount]!=amount+1 ? dp[amount] : -1;
}
}