【leetcode100】动态规划Java版本

发布于:2025-04-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

70. 爬楼梯

题目
思考的时候觉得情况很多,无从下手,卡在了找推导公式这一步。
看了随想录后知道以简单的三个阶梯来推导dp公式,为什么不是四个,五个为一组呢?因为题目要求的只能爬1个阶梯,或者2个阶梯,所以从第三个阶梯来思考!(注意看题目)

其他的错误的想法:以为dp[i]=dp[i-1]+1;是不对的,不是阶梯数加一,而是方法数!!!从第i-1到第i个只有dp[i-1]的方法。

错误的:n小于2时会数组越界

class Solution {
    public int climbStairs(int n) {
        int [] dp=new int[n+1];
        dp[1]=1;
        dp[2]=2; //n小于2时会数组越界
        for(int i=3;i<n+1;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

错误点2:dp[0]=1而不是0,因为dp[2]的时候是2
正确的:

class Solution {
    public int climbStairs(int n) {
        int [] dp=new int[n+1];
        dp[0]=1; //这里得是1,因为dp[2]的时候是2
        dp[1]=1; 
        for(int i=2;i<n+1;i++)
        {
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

118.杨辉三角

题目
这一次动态规划公式可以通过找规律想出来,但是不知道怎么对首尾的初始化以及以及每次如何加入到dp数组里。卡在初始化了。

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> dp=new ArrayList<>(numRows);
        dp.add(List.of(1));
        for(int i=1;i<numRows;i++)
        {
            List<Integer> row=new ArrayList<>(i+1);
            row.add(1);
            for(int j=1;j<i;j++) //注意这里是从1开始,i-1结尾,因为首位在循环外添加
            {
                row.add(dp.get(i-1).get(j-1)+dp.get(i-1).get(j));
            }
            row.add(1);
            dp.add(row);
        }
        return dp;

    }
}

198.打家劫舍

题目
这道题倒是自己思考出来了,就按着随想录的那五个步骤,心理默默的想每一步,然后把这道题思路和爬楼梯那道题想到一起的,dp【i】的数组就代表的是第i个房子最多打劫多少钱,然后又想到爬楼梯是每次一个或者两个,这里是如果也以三个为最小组的话,就是要么看dp[i-1]大还是dp[i-2]+nums[i]大,也就是第i个是根据前一个的结果或者前两个的结果再跳一个阶梯的结果,看谁更大。

class Solution {
    public int rob(int[] nums) {
        if(nums.length==1)
           return nums[0];
        int[] dp=new int[nums.length];
        dp[0]=nums[0];
        dp[1]=Math.max(nums[0],nums[1]);
        for(int i=2;i<nums.length;i++)
        {
            dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
        }
        return dp[nums.length-1];
    }
}

279.完全平方数

题目
我的思路:
想到的就是第dp[i]表示的是构成它的完全平方数最小的数量,它由前一步dp[i-1]的方法数再加上dp[i-dp[i-1]](这里是错误的想法)的方法数,然后卡住的地方是一个数由完全平方数相加而成的情况很多,不知道如何判断,只能想到遍历但是感觉很奇怪。还写另一个函数来专门判断是否是完全平方数。这一步答案的处理是完全平方数是在[1,根号i]区间内的。
**注意:**一定也要注意i的含义!!
我写的不对的:(没写完)

//不对!!!!!
class Solution {
     public static boolean isPerfectSquare(int number) {
        if (number < 0) {
            return false; // 负数不能开方
        }
        // 计算平方根并将其转换为整数
        double sqrt = Math.sqrt(number);
        
        // 判断平方根的整数部分与平方根是否相等
        return sqrt == (int) sqrt;
    }
    public int numSquares(int n) {
        int[] dp=new int[n+1];
        dp[1]=1;
        dp[4]=4;
        dp[i]=Math.min(dp[i-1]+dp[i-dp[i-1]]);
    }
}

官方正确答案:

class Solution {
    public int numSquares(int n) {
        int[] dp=new int[n+1];  //初始化的意义
        for(int i=1;i<n+1;i++)
        {
            int minnum=Integer.MAX_VALUE; 
            for(int j=1;j*j<=i;j++)//不是j<Math.sqrt(i) ,这一步就是看的是sqrt(i)有多少种方法,然后后面dp[i]=minnum+1就是相当于把sqrt(i)平方了
            {
                minnum=Math.min(minnum,dp[i-j*j]); //用减的形式表示和
            }
            dp[i]=minnum+1;
        }
        return dp[n];
    }
}

其他知识点:
Integer.MIN_VALUE: 表示 int 类型的最小值。
Long.MIN_VALUE: 表示 long 类型的最小值。
Double.MIN_VALUE: 表示 double 类型的最小正值。
Float.MIN_VALUE: 表示 float 类型的最小正值。
对于浮点类型的最小值,你还可以使用 Double.NEGATIVE_INFINITY 和 Float.NEGATIVE_INFINITY 来表示负无穷大。

322.零钱兑换

题目
没有思路,还是没有推导出来动态规划公式。这里的技巧:

  1. 初始化:不是默认设置为0,而是为amount+1
    Arrays.fill(dp,amount+1);//这里是每一个值都比amount大1,是为了标记有没有这个答案,即使中间的没有,那么最终的结果dp[amount]会比amount大的
  2. 有了1之后,动态规划公式的比较就是比较dp[i]和dp[i-coins[j]+1哪个更小了,如果是默认为0,就不能比较了。注意这个+1和上面题目完全平方数是一样的。

官方答案:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp=new int[amount+1]; 
        Arrays.fill(dp,amount+1);
        dp[0]=0;
        for(int i=1;i<amount+1;i++)
        {
            int mincoin=Integer.MIN_VALUE;
            for(int j=0;j<coins.length;j++)
            {
                if(coins[j]<=i)//肯定比i小
                {
                    dp[i]=Math.min(dp[i],dp[i-coins[j]]+1); //要么没有也就是dp[i](这里肯定就比amount大),要么是第i的数减去coins[j]加一(也就是加的coins[j]的值这一项)
                }
            }
        }
        return dp[amount]>amount?-1:dp[amount];
    }
}

其他:
数组初始化:

int[] dp=new int[amount+1]; 
Arrays.fill(dp,amount+1);

Java8之后可以用Arrays.setAll()

int[] arr = new int[5];
 // 使用 Arrays.setAll() 来初始化数组为 7
 Arrays.setAll(arr, i -> 7);

二维数组:

        int[][] arr = new int[3][3];        
        // 使用 Arrays.fill() 填充每一行
        for (int i = 0; i < arr.length; i++) {
            Arrays.fill(arr[i], 7);
        }

300.最长递增子序列

题目
没有思路,看了答案,感觉这种题目都是先考虑前一项(像爬楼梯)即dp[i-1]或者[0…dp[j]] j<i 这种来递推,考虑的时候会假设前面的那些已经求好了,有点像数学归纳法,假设第k-1项符合,然后证明第k项。

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0)
            return 0;
        int[] dp=new int[nums.length];
        Arrays.fill(dp,1);
        int maxnum=1; //而不是Integer.MIN_VALUE
      //  dp[0]=1;
        for(int i=1;i<nums.length;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(nums[j]<nums[i])
                {
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            maxnum=Math.max(dp[i],maxnum);              
            
        }
        return maxnum;
    }
}

139.单词拆分

题目

152.乘积最大子数组

题目
思路错误:

  1. 错误的:
dp[i]=Math.max(dp[i-1],dp[i-1]*nums[i]); //要连续,所以不对

2.错误的: 只要变小就要从这个小的从头开始计算最大的连续值
又想的是要连续的话,如果后一个和前一个相乘结果变小了,那就段了,从dp[i]从头开始了,先把用nums数组初始化dp,dp[i]表示的就是i前面最大的连续乘积,但是这种方法也不对,比如例子:【-2,3,-4】我的输出就是3,而正确结果是24,错误原因:我相当于把nums整个数组分割成每一个小段了,只比较了的dp[i-1]*nums[i]和dp[i-1],相当于只考虑了前一个数字,没有整个考虑。
【看官网原因更清晰】

//错误的!!!
class Solution {
    public int maxProduct(int[] nums) {
        int[] dp=Arrays.copyOf(nums,nums.length);
        int maxnum=nums[0];
        for(int i=1;i<nums.length;i++)
        {
            if(dp[i-1]*nums[i]>dp[i-1])
            {
                dp[i]=dp[i-1]*nums[i];
            }
        }
        for(int i=1;i<nums.length;i++)
        {
            maxnum=Math.max(maxnum,dp[i]);
        }
        return maxnum;
    }
}

乘了nums[i]可能变大也可能变小,如果是负数的话想让负数更多,如果是正数的话想让正数更多,所以维护两个数组,分别看最大的数组和最小的数组乘了nums[i]后,谁更大,为什么和nums[i]也要比较呢?

  int[] maxdp=int [nums.length];
        maxdp[0]=nums[0];
        mindp[0]=nums[0];
        for(int i=1;i<nums.length;i++)
        {
            maxdp=Math.max(Math.max(maxdp[i-1]*nums[i],mindp[i-1]*nums[i]),nums[i]);//要从nums[i]开始重新计算连续的
            mindp=Math.min(Math.min(maxdp[i-1]*nums[i],mindp[i-1]*nums[i]),nums[i]);
        }
        return