四十五天
爬楼梯(完全背包应用)

public class Solution {
public int ClimbStairs(int n) {
int[] dp = new int[n + 1];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= 2; j++) {
if (i - j >= 0) dp[i] += dp[i - j];
}
}
return dp[n];
}
}
零钱兑换
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
public class Solution {
public int CoinChange(int[] coins, int amount) {
if(amount == 0){return amount;}
int[] dp = new int[amount+1];
Array.Fill(dp,amount+1);
dp[0] = 0;
for(int i=0;i<coins.Length;i++){
for(int j=coins[i];j<=amount;j++){
dp[j] = Math.Min(dp[j-coins[i]]+1,dp[j]);
}
}
if(dp[amount]==amount+1){return -1;}
return dp[amount];
}
}
coins[] = {1,2,5}
i=0 dp[] = {0,1,2,3,4,5,6,7,8,9,10}
i=1 dp[] = {0,1,1,2,3,4,5,6,7,8,9}
i=2 dp[] = {0,1,1,2,3,1,2,3,4,2,3}
完全平方数
public class Solution {
public int NumSquares(int n) {
int[] dp = new int[n+1];
Array.Fill(dp,n+1);
dp[0] = 0;
for(int i=0;i<=n;i++){
for(int j=1;j*j<=i;j++){
dp[i] = Math.Min(dp[i-j*j]+1,dp[i]);
}
}
return dp[n];
}
}
四十六天
单词拆分
public class Solution {
public bool WordBreak(string s, IList<string> wordDict) {
HashSet<string> wordset = new HashSet<string>(wordDict);
bool[] dp = new bool[s.Length+1];
dp[0] = true;
for(int i=1;i<=s.Length;i++){
for(int j=0;j<i;j++){
string word = s.Substring(j,i-j);
if(wordset.Contains(word) && dp[j]){
dp[i] = true;
break;
}
}
}
return dp[s.Length];
}
}
背包问题总结:(到今天这些题没有一道是完全懂的,写个提醒)

四十七天
打家劫舍
递推公式:
决定dp[i]的因素就是第i房间偷还是不偷。
如果偷第i房间,那么dp[i] = dp[i - 2] + nums[i] ,即:第i-1房一定是不考虑的,找出 下标i-2(包括i-2)以内的房屋,最多可以偷窃的金额为dp[i-2] 加上第i房间偷到的钱。
如果不偷第i房间,那么dp[i] = dp[i - 1],即考虑i-1房,(注意这里是考虑,并不是一定要偷i-1房,这是很多同学容易混淆的点)
然后dp[i]取最大值,即dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
public class Solution {
public int Rob(int[] nums) {
if (nums.Length == 0) return 0;
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 - 2] + nums[i], dp[i - 1]);
}
return dp[nums.Length - 1];
}
}
nums = {2,3,9,7,1}
i = 2 3 4
dp[i] = 2 3 11 11 12
打家劫舍||
public class Solution {
public int Rob(int[] nums) {
if(nums.Length == 0)return 0;
if(nums.Length == 1)return nums[0];
int result1 = rob(nums,0,nums.Length-2);//不包含尾部元素的情况
int result2 = rob(nums,1,nums.Length-1);//不包含首部元素的情况
//不包含首尾元素的情况已经包含在上面两种情况
return Math.Max(result1,result2);
}
public int rob(int[] nums,int start,int end){
if(start == end)return nums[start];
int[] dp = new int[nums.Length];
dp[start] = nums[start];
dp[start+1] = Math.Max(nums[start],nums[start+1]);
for(int i=start+2;i<=end;i++){
dp[i] = Math.Max(dp[i-2]+nums[i],dp[i-1]);
}
return dp[end];
}
}
打家劫舍|||
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public int Rob(TreeNode root) {
int[] result = RobTree(root);
return Math.Max(result[0], result[1]);
}
//长度为2的数组,0为偷,1为不偷
private int[] RobTree(TreeNode cur) {
if (cur == null) return new int[]{0, 0};
int[] left = RobTree(cur.left);
int[] right = RobTree(cur.right);
//将当前节点的值(cur.val)、左子树中不偷当前节点的最大值(left[0])和右子树中不偷当前节点的最大值(right[0])相加,得到偷当前节点的最大值。
int val1 = cur.val + left[0] + right[0];
//分别取左子树中偷当前节点的最大值和不偷当前节点的最大值中的较大值(Math.Max(left[0], left[1])),再加上右子树中偷当前节点的最大值和不偷当前节点的最大值中的较大值(Math.Max(right[0], right[1])),得到不偷当前节点的最大值。最终,返回的是一个数组,其中第一个元素是不偷当前节点的最大值,第二个元素是偷当前节点的最大值。
int val2 = Math.Max(left[0], left[1]) + Math.Max(right[0], right[1]);
return new int[]{val2, val1};
}
}
四十八天
买卖股票的最佳时机
递推公式:
如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来
- 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
- 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i]
那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);
如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来
- 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
- 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
public class Solution {
public int MaxProfit(int[] prices) {
if(prices.Length == 0)return 0;
int[,] dp = new int[prices.Length,2];
//0为持有,1为不持有
dp[0,0] -= prices[0];
dp[0,1] = 0;
for(int i=1;i<prices.Length;i++){
dp[i,0] = Math.Max(dp[i-1,0],-prices[i]);
dp[i,1] = Math.Max(dp[i-1,1],prices[i]+dp[i-1,0]);
}
return dp[prices.Length-1,1];
}
}
买卖股票的最佳时机||(动态规划)
122. 买卖股票的最佳时机 II - 力扣(LeetCode)
在121. 买卖股票的最佳时机 (opens new window)中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。
而本题,因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。
那么第i天持有股票即dp[i][0],如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金减去今天的股票价格 即:dp[i - 1][1] - prices[i]。(除了这一块不一样,其他的逻辑一样)
public class Solution {
public int MaxProfit(int[] prices) {
if(prices.Length == 0)return 0;
int[,] dp = new int[prices.Length,2];
//0为持有,1为不持有
dp[0,0] -= prices[0];
dp[0,1] = 0;
for(int i=1;i<prices.Length;i++){
dp[i,0] = Math.Max(dp[i-1,0],dp[i-1,1]-prices[i]);
dp[i,1] = Math.Max(dp[i-1,1],prices[i]+dp[i-1,0]);
}
return dp[prices.Length-1,1];
}
}
四十九天
买卖股票的最佳时机|||
123. 买卖股票的最佳时机 III - 力扣(LeetCode)
这题主要有5个状态要写,
- 没有操作
- 第一次持有股票
- 第一次不持有股票
- 第二次持有股票
- 第二次不持有股票
递推公式:(买入减prices,卖出加prices)
达到dp[i][1]状态,有两个具体操作:
- 操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
- 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]
那么dp[i][1]究竟选 dp[i-1][0] - prices[i],还是dp[i - 1][1]呢?
一定是选最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);
public class Solution {
public int MaxProfit(int[] prices) {
if(prices.Length == 0)return 0;
int[,] dp = new int[prices.Length,5];
dp[0,1] = -prices[0];
dp[0,3] = -prices[0];
for(int i=1;i<prices.Length;i++){
dp[i,0] = dp[i-1,0];
dp[i,1] = Math.Max(dp[i-1,1],dp[i-1,0]-prices[i]);
dp[i,2] = Math.Max(dp[i-1,2],dp[i-1,1]+prices[i]);
dp[i,3] = Math.Max(dp[i-1,3],dp[i-1,2]-prices[i]);
dp[i,4] = Math.Max(dp[i-1,4],dp[i-1,3]+prices[i]);
}
return dp[prices.Length-1,4];
}
}
买卖股票的最佳时机IV
188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
在上面123这题的基础上换了k次买入卖出,只要写好k次买入卖出的循环就行。
public class Solution {
public int MaxProfit(int k, int[] prices) {
if (prices.Length == 0) return 0;
int[,] dp = new int[prices.Length + 1, k * 2 + 1];
for (int i = 0; i < k; i++)
{
dp[0, i * 2 + 1] = -prices[0];
}
for (int i = 1; i < prices.Length; i++)
{
dp[i, 0] = dp[i - 1, 0];
for (int j = 1; j < k * 2 + 1; j++)
{
dp[i, j] = Math.Max(dp[i - 1, j], dp[i, j - 1] + (int)Math.Pow(-1, j) * prices[i]);
}
}
return dp[prices.Length-1, k * 2];
}
}
1130

被折叠的 条评论
为什么被折叠?



