1-leetcode70. 爬楼梯
注意:×
- 理解这里的
dp[i] = dp[i-1]+dp[i-2]
- 到我这个台阶,可以从低一层的来,也可以从低两层的来,直接将二者相加就好了
public int climbStairs(int n) {
if (n<=2){
return n;
}
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for (int i = 3; i < dp.length; i++) {
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
}
2-leetcode118. 杨辉三角
注意:×
- 要点1:
List<Integer> preList = res.get(res.size()-1);
- 要点2:一定要注意这里是
preList.size()-1;
!!!! - 要点3:
getNextRow
函数中要对List的前后手动的添加1
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res = new ArrayList<>();
if (numRows < 1) {
return res;
}
List<Integer> first = new ArrayList<>();
first.add(1);
res.add(first);
for (int i = 2; i <= numRows; i++) {
List<Integer> preList = res.get(res.size()-1);
res.add(getNextRow(preList));
}
return res;
}
private List<Integer> getNextRow(List<Integer> preList) {
List<Integer> thisRow = new ArrayList<>();
thisRow.add(1);
for (int i = 0; i < preList.size()-1; i++) {
thisRow.add(preList.get(i)+preList.get(i+1));
}
thisRow.add(1);
return thisRow;
}
3-leetcode198. 打家劫舍
注意:√
- 这题还是比较简单的
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
- 已知1和2推出3
public int rob(int[] nums) {
int n = nums.length;
if (n == 1){
return nums[0];
}
int[] dp = new int[n+1];
// base case
dp[0] = 0;
dp[1] = nums[0];
for (int i = 2; i < n + 1; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
}
return dp[n];
}
4-leetcode279. 完全平方数
注意:√
- 跟5-leetcode322很像,零钱是要在for循环内部遍历coins中判断当前需要的
i - coin < 0
- 这里是第二轮遍历判断
j*j<=i
- 注意这里的状态转移
dp[i] = Math.min(dp[i], dp[i-j*j]+1);
public int numSquares(int n) {
int[] dp = new int[n + 1];
Arrays.fill(dp, n + 1);
// base case
dp[0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j*j <= i; j++) {
dp[i] = Math.min(dp[i], dp[i-j*j]+1);
}
}
return dp[n]==n+1? -1:dp[n];
}
5-leetcode322. 零钱兑换
注意:×
- 注意要给初始值,这里给
amount+1
是因为后面需要1+dp[i-coin]
防止溢出 - 因为零钱没有数量限制,所以在
fori
的循环内部需要对各种类型的零钱进行一个遍历
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount+1];
Arrays.fill(dp,amount+1);
// base case
dp[0] = 0;
// 转移
for (int i = 1; i < dp.length; i++) {
for (int coin : coins) {
if (i - coin < 0){
continue;
}
dp[i] = Math.min(dp[i], 1+dp[i-coin]);
}
}
return (dp[amount] == amount+1)? -1: dp[amount];
}
6-leetcode139. 单词拆分
注意:×
- 问题很多,要非常小心的看。别的方法比较乱,没有这个简洁易懂
- 直接将List丢到HashSet中可以自动转换
- boolen数组
- 注意设置base case
- dp[i]=true的条件是
dp[j] && hashSet.contains(s.substring(j,i))
- 注意为什么substring的取值是(j,i),因为substring是左闭右开的区间
public boolean wordBreak(String s, List<String> wordDict) {
int n = s.length();
HashSet<String> hashSet = new HashSet<>(wordDict);
boolean[] dp = new boolean[n + 1];
// base case
dp[0] = true;
for (int i = 1; i < n + 1; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && hashSet.contains(s.substring(j,i))){
dp[i] = true;
break;
}
}
}
return dp[n];
}
7-leetcode300. 最长递增子序列
注意:×
- 子序列是可以间断的
dp[i]
表示到这个位置nums[0...i]
的最长递增子序列的值是多少
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
int res = Integer.MIN_VALUE;
Arrays.fill(dp, 1);
for (int i = 0; i < dp.length; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], 1 + dp[j]);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
8-leetcode152. 乘积最大子序列
注意:×
- 这个题目和lc53的“最大子数组和”不一样,lc53是直接
dp[i] = Math.max(nums[i], dp[i-1]*nums[i]);
就好了,但是这里是乘积 - 乘积需要注意,当前状态下可能是一个负数乘积,到后面可能再乘一个负数就转正了
- 因此需要一个维护正数的值一个维护负数的值
- 编代码的时候注意,
nmin = Math.min(
和nmax = Math.max(
public int maxProduct(int[] nums) {
int n = nums.length;
if (n == 1) {
return nums[0];
}
int nmin = 1;
int nmax = 1;
int res = Integer.MIN_VALUE;
for (int i = 0; i < n; i++) {
if (nums[i]<0){
int tmp = nmin;
nmin = nmax;
nmax = tmp;
}
nmin = Math.min(nmin*nums[i], nums[i]);
nmax = Math.max(nmax*nums[i], nums[i]);
res = Math.max(res, nmax);
}
return res;
}
作者:画手大鹏
链接:https://leetcode.cn/problems/maximum-product-subarray/solutions/7561/hua-jie-suan-fa-152-cheng-ji-zui-da-zi-xu-lie-by-g/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
9-leetcode416. 分割等和子集
注意:×
- 二维的dp
- 注意对sum是奇数的时候直接进行判断
- 在状态转移的时候,如果
j-nums[i-1]
表示当前的容量减去目前要加的货物量,如果不够,那就跟不选择这个货物的状态是一样的 - 如果还能装,那就从不装这个货物和装这个货物刚好装满二者之间选一个
- dp 数组的定义:dp[i][j] = x 表示,对于前 i 个物品,当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满。
- 说实话还是有点蒙,慢慢揣摩
public boolean canPartition(int[] nums) {
int n = nums.length;
int sum = 0;
for (int num : nums) {
sum = sum + num;
}
if (sum%2!=0){
return false;
}
sum = sum / 2;
// base case
boolean[][] dp = new boolean[n + 1][sum + 1];
for (int i = 0; i < n + 1; i++) {
dp[i][0] = true;
}
for (int i = 0; i < sum + 1; i++) {
dp[0][i] = false;
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < sum + 1; j++) {
if (j-nums[i-1]<0) {
// 容量不足
dp[i][j] = dp[i-1][j];
}else {
dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
}
}
}
return dp[n][sum];
}