算法学习 | 动态规划~大事化小、小事化了

目录

动态规划

动态规划相关OJ题

斐波那契数列

拆分词句

三角形 

不同的路径数目(一) 


动态规划

动态规划是分治思想的延伸,在将大问题化解为小问题的分治过程中,保存对这些小问题已经处理好的结果,并供后面处理更大规模的问题时直接使用这些结果.

动态规划具备如下三个特点:

①把原来的问题分解成了几个相似的子问题;

②所有的子问题都只需要解决一次;

③储存子问题的解.

动态规划的本质就是对问题状态的定义和状态转移方程的定义,动态规划问题一般从以下四个角度考虑:①状态定义;②状态间的转移方程定义;③状态的初始化;④返回结果

动态规划一般适用于求最大值/最小值,判断可行或是不可行,求方案个数等等

动态规划相关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<String> 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<ArrayList<Integer>> triangle) {
        if (triangle.isEmpty()) {
            return 0;
        }
        List<List<Integer>> 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];
    }
}

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li_yizYa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值