本质上感觉是一个相加的问题,状态的转换,由前一种状态推至下一种状态
509. 斐波那契数
较为简单
746. 使用最小花费爬楼梯
62. 不同路径
一开始写的时候被吓到了,但是发现听完一半之后再写还是比较容易的
对于我而言主要是找到逻辑,
class Solution {
public int uniquePaths(int m, int n) {
if (m <= 1 || n <=1){
return 1;
}
int[][] result = new int[m][n];
result[0][0] = 0;
result[1][0] = 1;
result[0][1] = 1;
for (int i = 0;i < m;i++){
for (int j = 0;j < n; j++){
if (i > 0 && j > 0){
result[i][j] = result[i][j-1] + result[i-1][j];
}else if (i == 0 && j > 0 && j!=1){
result[i][j] = result[i][j-1];
}else if (j == 0 && i > 0 && i!=1){
result[i][j] = result[i-1][j];
}
}
}
return result[m-1][n-1];
}
}
63. 不同路径 II
秒杀,和前者逻辑差不多,要注意初始化就好
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
if (m <= 1 || n <=1){
for (int i = 0;i < m; i++){
for (int j = 0;j <n; j++){
if (obstacleGrid[i][j] == 1){
return 0;
}
}
}
return 1;
}
int[][] result = new int[m][n];
if (obstacleGrid[0][0] == 1){
return 0;
}else{
result[0][0] = 0;
}
if (obstacleGrid[1][0] == 1){
result[1][0] = 0;
}else{
result[1][0] = 1;
}
if (obstacleGrid[0][1] == 1){
result[0][1] = 0;
}else{
result[0][1] = 1;
}
for (int i = 0;i < m;i++){
for (int j = 0;j < n; j++){
if (obstacleGrid[i][j] == 1){
result[i][j] = 0;
}else if (i > 0 && j > 0){
result[i][j] = result[i][j-1] + result[i-1][j];
}else if (i == 0 && j > 0 && j!=1){
result[i][j] = result[i][j-1];
}else if (j == 0 && i > 0 && i!=1){
result[i][j] = result[i-1][j];
}
}
}
return result[m-1][n-1];
}
}
别人的写法,比较nb
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
int[][] dp = new int[m][n];
//如果在起点或终点出现了障碍,直接返回0
if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
return 0;
}
for (int i = 0; i < m && obstacleGrid[i][0] == 0; i++) {
dp[i][0] = 1;
}
for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
dp[0][j] = 1;
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
}
}
return dp[m - 1][n - 1];
}
}
343. 整数拆分
再写吧。。累了
96. 不同的二叉搜索树
416. 分割等和子集
背包问题 - 我真心实意的感觉这个像玄学
明明其实不太明白呢,但神不知鬼不觉的就写出来了,我的天,,,,,
为什么要求max呢????我真的不理解
class Solution {
public boolean canPartition(int[] nums) {
int sum =0;
for (int i =0;i < nums.length;i++){
sum += nums[i];
}
if (sum % 2 != 0){
return false;
}else{
sum = sum/2;
}
int dp[][] = new int[nums.length][sum + 1];
for (int j = 0;j < sum + 1;j++){
if (j >= nums[0]){
dp[0][j] = nums[0];
}
}
for (int i = 1;i < nums.length;i++){
for (int j = 1;j < sum + 1;j++){
if (j < nums[i])
dp[i][j] = dp[i - 1][j];
else
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);
}
}
if(dp[nums.length - 1][sum] == sum){
return true;
}else{
return false;
}
}
}
1049. 最后一块石头的重量 II
这里使用了一维的数组来动态的更新所能承受的最大的重量,要点是要理解能不能把石头平均分为两份以换取最大值,并且在从0到sum/2的最大重量下,所可以放的重量的最大值,总体来说还是很难想的。。
第二个循环的j需要注意
最后的除也要注意一下
代码
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for (int i = 0;i < stones.length;i++){
sum = sum + stones[i];
}
int total = sum;
sum = sum/2;
int dp[] = new int[sum+1];
for (int i = 0;i < stones.length;i++){
for (int j = sum; j >= stones[i];j--){
dp[j] = Math.max(dp[j],dp[j - stones[i]]+stones[i]);
}
}
int remind = total - 2 * dp[sum];
return Math.abs(remind);
}
}
494. 目标和
这个题还不是很了解,继续加油吧
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for (int i = 0;i < nums.length;i++){
sum +=nums[i];
}
int left = (target + sum)/2;
if ((target + sum) % 2 == 1 || Math.abs(target) > sum){
return 0;
}
int dp[] = new int[left + 1];
dp[0] = 1;
for (int num : nums) { /
for (int j = left; j >= num; j--) {
dp[j] += dp[j - num];
}
}
return dp[left];
}
}
dp[0] = 1;这个是为什么
ChatGPT
在这类动态规划问题中,
dp[0] = 1;
的设置是非常关键的。这里dp[0]
表示的是在没有任何数字可选的情况下,和为0的方法数。即,不选择任何数字,就可以达成和为0的目标。这是基础情况(Base Case)。为什么要将
dp[0]
设为1呢?这是因为在动态规划的过程中,我们利用之前计算的结果来计算当前的结果。当我们说dp[0] = 1
时,我们实际上是在说:“如果没有数字可选(即子集为空),只有一种方式使得总和为0,那就是什么都不选。”这个初始条件对于后续的递推非常重要。例如,当我们在计算
dp[i]
(即和为i
的方法数)时,如果当前考虑的数字为num
,我们查看dp[i - num]
的值(即在不包括当前数字的情况下,和为i - num
的方法数)。dp[0] = 1
保证了在递推开始的时候,我们有一个有效的起点。如果没有这个基础情况,那么递推就无从开始,因为我们总需要一个起始的参考点。在这个特定问题中,我们使用
dp
数组来记录达到特定和的方法数量。因此,开始时设定dp[0] = 1
,表示对于目标和为0,已经有了一种方法(即不选择任何元素)。这使得在动态规划过程中,每当我们考虑加入一个新的元素num
时,都可以通过查看dp[之前的和]
来更新dp[新的和]
,因为从dp[0]
开始的这个基础上,我们可以建立起所有的解决方案。
474.一和零
略
完全背包
518. 零钱兑换 II
377. 组合总和 Ⅳ
说实话我感觉背包问题我还是迷迷糊糊的,有一种一言难尽的感觉。
就是,总是感觉不得要领,最近状态不太好,估计还要再做几遍。
class Solution {
public int combinationSum4(int[] nums, int target) {
int dp[] = new int[target + 1];
dp[0] = 1;
for (int i = 0 ;i <= target ;i++){
for (int j = 0;j < nums.length;j++){
if (i >= nums[j]){
dp[i] = dp[i] + dp[i-nums[j]];
}
}
}
return dp[target];
}
}
因此这里暂时把后续的背包问题跳过一下,要不然太痛苦了。
打家劫舍问题
问题三还没有解决,有时间来总结一下
买卖股票的问题
感觉这个解释的很好