算法之《动态规划》练习解析

动态规划概念篇

定义:动态规划是分治思想的延伸,通俗一点来说就是大事化小,小事化无的艺术。通过拆分问题、定义问题状态和状态间的关系,让问题用递推的方式得到结局;
特点和考虑维度
①把原来的问题分解成了几个相似的子问题。
② 所有的子问题都只需要解决一次。
③ 储存子问题的解。
动态规划的本质,是对问题状态的定义和状态转移方程的定义
动规一般都需要和数组搭配使用
动态规划问题一般从以下四个角度考虑:
状态定义、状态间的转移方程定义、状态的初始化、 返回结果
适用场景:最大/最小值 、可行不可行、是不是、方案个数等

个人看法:其实这些概念我感觉看看就好,因为在你没有做很多动规题之前,再看这些概念也是纸上谈兵,也不会对动规有很好的理解和掌握,只有做的多了,自不然就明白动规是咋回事,也就会用了;emmm,我滚去刷题了,我会把题目链接附上,大家感兴趣可以去做一下,毕竟实践出真知哈

实际练习篇

1、斐波那契数列

题目链接:牛客网剑指offerJZ7;现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)
解析:这个是最简单也是最经典的动规问题,拿上面的概念去套,要求第n个,相似的子问题,就是需要知道第n项的前两个,他们之间可以递推,

public class Solution {
    public int Fibonacci(int n) {
        if(n<=0){
            return 0;
        }
        int arr[]=new int[n+1];//保存子问题的解
        arr[0]=0;//这两个就是状态的初始化
        arr[1]=1;
        for(int i=2;i<=n;i++){
            arr[i]=arr[i-1]+arr[i-2];//状态转移方程
        }
        return arr[n];
    }
}

2、牛妹的蛋糕

题目链接:牛客网-牛牛吃蛋糕;众所周知,牛妹非常喜欢吃蛋糕。第一天牛妹吃掉蛋糕总数三分之一(向下取整)多一个,第二天又将剩下的蛋糕吃掉三分之一多一个,以后每天吃掉前一天剩下的三分之一多一个,到第n天准备吃的时候只剩下一个蛋糕。牛妹想知道第一天开始吃的时候蛋糕一共有多少呢?
解析:最后一天时剩一个,从最后一天往前推,找递推公式,分析题意,假设今天有蛋糕y个,前一天有x个。则y = x*2/3-1,所以就能得出x=3(y+1)/2。

import java.util.*;
public class Solution {
    public int cakeNumber (int n) {
        if(n==1){
            return 1;
        }
        int sum=1;//初始值
        for(int i=n;n>1;n--){//从第n天开始往前推
            sum=3*(sum+1)/2;
        }
        return sum;
    }
}

3、连续子数组的最大和

题目链接:牛客网-剑指offer原题:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
解析:从第一个开始,每往进加一个就要去判断现在的总和跟没加之前相比哪个大;

public class Solution {
    public int FindGreatestSumOfSubArray(int[] array) {
        int result=array[0];//保留当前最大值
        int max=array[0];//包含arr[i]的连续数组最大值
        for(int i=1;i<array.length;i++){
            max=Math.max(max+array[i],array[i]);
            result=Math.max(max,result);
        }
        return result;
    }
}

4、单词分割

题目链接:牛客网-单词分割;给定一个字符串s和一组单词dict,判断s是否可以用空格分割成一个单词序列,使得单词序列中所有的单词都是dict中的单词(序列可以包含一个或多个单词)。
例如:给定s=“leetcode”;dict=[“leet”, “code”]。返回true,因为"leetcode"可以被分割成"leet code".
解析:分解成子问题就是数组中后面的包含在内,那位于前面的一定要包含在内,所以我们就需要去判断每一个是否包含在内;

import java.util.Set;
public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        boolean[] arr=new boolean[s.length()+1];
        //用boolean类型的数组去记录单个的是否被包含
        arr[0]=true;//假设一个初始值
        for(int i=1;i<=s.length();i++){
            for(int j=i-1;j>=0;j--){
                if(arr[j]&&dict.contains(s.substring(j,i))){
                    arr[i]=true;
                    break;
                }
            }
        }
        return arr[s.length()];
    }
}

5、机器人走方格

题目链接:牛客网-机器人走方格;一个机器人在m×n大小的地图的左上角(起点,下图中的标记“start"的位置)。机器人每次向下或向右移动。机器人要到达地图的右下角。(终点,下图中的标记“Finish"的位置)。
可以有多少种不同的路径从起点走到终点?
在这里插入图片描述
解析:划分为子状态就是从(0,0)到达(1,0),(1,1),(2,1),…(m-1,n-1)的路径数;F(i,j): 从(0,0)到达F(i,j)的路径数
状态递推:F(i,j) = F(i-1,j) + F(i,j-1)

import java.util.*;
public class Solution {
    public int uniquePaths (int m, int n) {
        if(m==0||n==0)
            return 0;
        int[][] arr=new int[m][n];
        for(int i=1;i<n;i++){//两个for循环进行初始化
            arr[0][i]=1;
        }
        for(int i=0;i<m;i++){
            arr[i][0]=1;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                arr[i][j]=arr[i-1][j]+arr[i][j-1];
                //状态转移方程,这一步可以有前面这两部走过来
            }
        }
        return arr[m-1][n-1];//返回结果
    }
}

6、01背包问题

题目链接:背包问题,笔试还是很常见的;有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值。问最多能装入背包的总价值是多大?
样例 1:
输入: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]
输出: 9
解释: 装入 A[1] 和 A[3] 可以得到最大价值, V[1] + V[3] = 9
解析
F(i, j)是前i个物品放入大小为j的背包中所获得的最大价值
状态递推:对于第i个商品,有一种例外,装不下,两种选择,放或者不放; 如果装不下:此时的价值与前i-1个的价值是一样的
F(i,j) = F(i-1,j)
如果可以装入:需要在两种选择中找最大的
F(i, j) = max{F(i-1,j), F(i-1, j - A[i]) + V[i]}
F(i-1,j): 表示不把第i个物品放入背包中, 所以它的价值就是前i-1个物品放入大小为j的背包的最大价值
F(i-1, j - A[i]) + V[i]:表示把第i个物品放入背包中,价值增加V[i],但是需要腾出j - A[i]的大小放
第i个商品
初始化:第0行和第0列都为0,表示没有装物品时的价值都为0
F(0,j) = F(i,0) = 0
返回值:F(n,m)

public class Solution {
    public int backPackII(int m, int[] A, int[] V) {
        // write your code here
        int len=A.length;
        int[][] maxValue=new int[len+1][m+1];
        for(int i=0;i<=num;i++){//进行初始化
            maxValue[i][0]=0;
        }
        for(int i=1;i<=num;i++){
            maxValue[0][i]=0;
        }
        for(int i=1;i<=num;i++){
            for(int j=1;j<=num;j++){
                if(A[i-1]>j){//说明放不下
                    maxValue[i][j] = maxValue[i - 1][j];//装不下时value值不变
                }
                else{
                //能装下时看你愿不愿意装,装的话就腾出第i个物品
                    int new=maxValue[i-1][j-A[i-1]]+V[i-1];
                    maxValue[i][j]=Math.max(new,maxValue[i-1][j]);
                }
            }
        }
        return maxValue[num][m];
    }
}

7、回文串分割

题目链接:牛客网-回文串分割
给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数
例如:给定字符串s=“aab”,
返回1,因为回文分割结果[“aa”,“b”]是切割一次生成的。

import java.util.*;
public class Solution {
    public boolean isPal(String s,int start,int end){
        while(start<end){
            if(s.charAt(start)!=s.charAt(end)){
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
    public int minCut (String s) {
        int len=s.length();
        if(len==0){
            return 0;
        }
        int[] arr=new int[len+1];
        for(int i=0;i<=len;i++){//进行初始化
            arr[i]=i-1;
        }
        for(int i=1;i<=len;i++){
            for(int j=0;j<i;j++){
                if(isPal(s,j,i-1)){
                    arr[i]=Math.min(arr[i],arr[j]+1);
                }
            }
        }
        return arr[len];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值