1.零钱兑换
(1)dp[i]含义:定义dp[i] 为组成金额 i 所需最少的硬币数量
(2)找数组元素之间关系:,coins[j]表示可选的硬币面值
(3)初始值(base case):dp[0]=0;
class Solution {
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount+1,amount+1);
dp[0] = 0;
for(int coin:coins)
for(int i = coin;i<=amount;i++)
dp[i] = min(dp[i],dp[i-coin]+1);
return dp[amount] == amount+1?-1:dp[amount];
}
};
2. 最大正方形
(1)dp[i][j]含义:matrix[i][j] 为右下角的正方形的最大边长
(2)关系:对于任意一点dp[i][j],由于该点是正方形的右下角,所以该点右边、下边、右下均不考虑,关心其左边、上边以及左上,也就是要推导dp[i][j]与dp[i][j-1]、dp[i-1][j]和dp[i-1][j-1]之间的关系,画图可得:
dp[i][j]=min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1])+1.
(3)初始值:如果 i 和 j 中至少有一个为 0,则以位置 (i, j)为右下角的最大正方形的边长只能是 1,因此 dp[i][j]= 1
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if(matrix.size()<1 || matrix[0].size()<1){
return 0;
}
int row=matrix.size();
int col=matrix[0].size();
int max=0;
vector<vector<int>> dp(row,vector<int>(col));
for(int i=0; i<row; i++){
for(int j=0; j<col; j++){
if(matrix[i][j]=='1'){
if(i==0 || j==0) dp[i][j]=1;
else dp[i][j]=min(min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
}
max = max > dp[i][j] ? max : dp[i][j];
}
}
return max*max;
}
};
3. 打家劫舍
思路:
对于第 k~(k>2)k (k>2) 间房屋,有两个选项:
偷窃第 k 间房屋,那么就不能偷窃第 k-1 间房屋,偷窃总金额为前 k-2 间房屋的最高总金额与第 k 间房屋的金额之和。
不偷窃第 k 间房屋,偷窃总金额为前 k-1间房屋的最高总金额。
(1)dp[i]含义:表示前 i 间房屋能偷窃到的最高总金额
(2)数组关系:dp[i]=max(dp[i−2]+nums[i],dp[i−1])
(3)边界条件:没有人家 dp[0]=0, 一户人家 dp[1]=nums[0]
class Solution {
public:
int rob(vector<int>& nums) {
int len=nums.size();
if(len==0) return 0;
int dp[len+1];
dp[0]=0;
dp[1]=nums[0];
for(int i=2; i<=len;i++){
dp[i]=max(dp[i-2]+nums[i-1], dp[i-1]);
}
return dp[len];
}
};
4. 买股票的最佳时机(只允许买一次,卖一次)
(1)dp[i]含义:dp[i] 表示前 i 天的最大利润
(2)转移方程:dp[i]=max(dp[i−1], prices[i]−min_price)
(3)边界条件:dp[0]=0;
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<1) return 0;
int min_price=prices[0];
vector<int> dp(n);
dp[0]=0;
for(int i=1; i<n; i++){
if(prices[i]<min_price){
min_price=prices[i];
}
dp[i]=max(dp[i-1],prices[i]-min_price);
}
return dp[n-1];
}
};
5. 买卖股票的最佳时机(不限买卖次数)
(1)dp[i][j]含义:索引为 i
的那一天(具有前缀性质,即考虑了之前天数的收益)能获得的最大利润; j则表示索引为 i
的那一天是持有股票,还是持有现金
(2)数组关系:
//第i天不持有股票 i-1天不持有且无操作 i-1天持有并在i天卖了+prices[i]
dp[i][0] = max( dp[i - 1][0], dp[i - 1][1] + prices[i]);
//第i天持有股票 i-1天持有且无操作 i-1天不持有但是买入了-prices[i]
dp[i][1] = max( dp[i - 1][1], dp[i - 1][0] - prices[i]);
(3)边界条件:起始的时候:什么都不做:dp[0][0]=0; 买入股票收益为负:dp[0][1]= -prices[0]
(4)注意本题返回值:dp[n-1][0]; 因为一定有dp[n-1][0] > dp[n-1][1]
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
if(n<1) return 0;
vector<vector<int>> dp(n,vector<int>(2));
dp[0][0]=0;
dp[0][1]=-prices[0];
for(int i=1; i<n; i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[n-1][0];
}
};