力扣100题——动态规划
爬楼梯
题目
70. 爬楼梯 - 力扣(LeetCode)
思路
动态规划关键在于写出状态转移方程,根据题目的意思每次能上一个台阶或两个台阶
- 用dp数组,dp[i]代表上到第i个台阶最多有几种的方法
- 那么很容易就可以推出 dp[i]=dp[i-1]+dp[i-2]
- 对dp数组进行初始化,根据推理,很容易可以得到dp[0]=0,dp[1]=1,dp[2]=2;
- 最后根据状态转移方程开始推dp的所有值
代码
public int climbStairs(int n) {
int[] dp = new int[n+1];
if(n==0){
return 0;
}
if(n<2){
return 1;
}
dp[0] = 0;
dp[1] = 1;
dp[2] = 2;
for(int i=3;i<=n;i++){
dp[i] = dp[i-1]+dp[i-2];
}
return dp[n];
}
杨辉三角
题目
118. 杨辉三角 - 力扣(LeetCode)
思路
根据题目给出的状态转移方程直接模拟实现,优化思路在于
- 去除二维数组:我们不需要预先声明一个二维数组,只需动态地生成每一行,并将结果存入最终的
List<List<Integer>>
。 - 直接填充每行元素:根据帕斯卡三角形的性质,第
i
行的第j
个元素为上一行的第j-1
个元素和第j
个元素之和。我们只需按这个规则生成每一行即可。 - 减少不必要的检查:当前代码中的一些边界条件检查和赋值可以通过初始化每行的第一个和最后一个元素为 1 来避免。
代码
直接模拟
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res = new ArrayList<>();
if(numRows==0){
return res;
}
int[][] result = new int[numRows][numRows];
for(int i=0;i<numRows;i++){
result[numRows-1][i]=1;
result[i][0]=1;
}
for(int i=0;i<numRows;i++){
for(int j=0;j<numRows;j++){
if(i==j){
result[i][j]=1;
}
}
}
for(int i=0;i<numRows;i++) {
for (int j = 0; j < numRows; j++) {
if((i-1)>=0&&(j-1)>=0&&result[i][j-1]>0&&result[i-1][j-1]>0){
result[i][j] = result[i-1][j-1]+result[i-1][j];
}
}
}
for(int i=0;i<numRows;i++) {
List<Integer> list = new ArrayList<>();
for (int j = 0; j < numRows; j++) {
if(result[i][j]>0){
list.add(result[i][j]);
}
}
res.add(list);
}
return res;
}
优化
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> res = new ArrayList<>();
for(int i=0;i<numRows;i++){
List<Integer> row = new ArrayList<>();
for(int j=0;j<i+1;j++){
if(j==0||j==i){
row.add(1);
}else{
row.add(res.get(i-1).get(j-1)+res.get(i-1).get(j));
}
}
res.add(row);
}
return res;
}
打家劫舍
题目
198. 打家劫舍 - 力扣(LeetCode)
思路
根据题目找出状态转移方程,使用dp数组保存记录,dp[i]即为当前偷的最大值
因为不能偷相邻的房子,所以dp[i] = max( dp[i-1] ,dp[i-2]+nums[i])
代码
public int rob(int[] nums) {
int n = nums.length;
if(nums.length==0){
return 0;
}
if(nums.length==1){
return nums[0];
}
int[] dp = new int[n];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i=2;i<n;i++){
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i]);
}
return dp[n-1];
}
完全平方数
题目
279. 完全平方数 - 力扣(LeetCode)
思路
- 定义状态:
dp[i]
表示和为i
的最少完全平方数数量。 - 状态转移方程:
- 对于每个
i
,我们尝试从较小的完全平方数开始,假设平方数为j * j
,则dp[i] = min(dp[i], dp[i - j * j] + 1)
。 dp[i - j * j]
表示剩余的部分i - j * j
的最少数量,再加上一个平方数j * j
。
- 对于每个
- 初始条件:
dp[0] = 0
,因为和为0
不需要任何平方数。
代码
public int numSquares(int n) {
int[] dp = new int[n+1];
Arrays.fill(dp, Integer.MAX_VALUE);
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];
}
零钱兑换
题目
322. 零钱兑换 - 力扣(LeetCode)
思路
- 和上一题类似,找出状态转移方程,
dp[i]
表示总额为i
的最少硬币数量。 - dp[i] = Math.min(dp[i], dp[i - coin] + 1);
代码
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount+1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for(int i=1;i<=amount;i++){
for (int coin : coins) {
if (i >= coin && dp[i - coin] != Integer.MAX_VALUE) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
}