按摩师
1.状态表示
dp【i】表示:到i位置时,此时最长时长
继续细化:在i位置选预约,或不选预约
f【i】:到i位置时,nums【i】必选的,最长时长
g【i】:到i位置时,nums【i】不选的,最长时长
2状态转移方程
f【i】=g【i-1】+nums【i】
g【i】=max(f【i-1】,g【i-1】)
3初始化
f【0】=nums【0】
g【0】=0
4.填表顺序
左往右
5.返回值
max(f【n-1】,g【n-1】)
class Solution {
public:
int massage(vector<int>& nums) {
int n=nums.size();
if(n==0)
return 0;
vector<int>f(n);
vector<int>g(n);
f[0]=nums[0];
for(int i=1;i<n;i++)
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[n-1],g[n-1]);
}
};
打家劫舍
1.dp表示:
f【i】:表示到i位置时,必偷i位置房屋,获得的最大金额
g【i】:表示到i位置时,不偷i位置房屋,获得的最大金额
f【i】=g【i-1】+nums【i】
g【i】=max(f【i-1】,g【i-1】)
f【0】=nums【0】
返回值
max(f【n-1】,g【n-1】)
class Solution {
public:
int rob(vector<int>& nums) {
int n =nums.size();
vector<int>f(n);
vector<int>g(n);
f[0]=nums[0];
for(int i=1;i<n;i++)
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[n-1],g[n-1]);
}
};
打家劫舍 II
环形问题转化
分情况偷0下标的和不偷0下标的
当偷【0】时,转化为打家劫舍一:从【2,n-2】的打家劫舍
不偷【0】,转化为从【1,n-1】的打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
int n=nums.size();
if(n==1)
return nums[0];
else if(n==2)
return max(nums[0],nums[1]);
else if(n==3)
return max(max(nums[0],nums[1]),nums[2]);
int a=nums[0]+_rob(nums,2,n-2);
int b=_rob(nums,1,n-1);
return max(a,b);
}
int _rob(vector<int>&nums,int left,int right)
{
int n=nums.size();
vector<int>f(n);
vector<int>g(n);
f[left]=nums[left];
for(int i=left+1;i<=right;i++)
{
f[i]=g[i-1]+nums[i];
g[i]=max(g[i-1],f[i-1]);
}
return max(f[right],g[right]);
}
};
删除并获得点数
转换为打家劫舍问题
用一个arr数组:arr【i】表示i出现的总和
class Solution {
public:
const int N=10001;
int deleteAndEarn(vector<int>& nums)
{
int n=nums.size();
vector<int>arr(N);
for(int v:nums)
{
arr[v]+=v;
}
vector<int>f(N);
vector<int>g(N);
for(int i=1;i<N;i++)
{
f[i]=g[i-1]+arr[i];
g[i]=max(g[i-1],f[i-1]);
}
return max(f[N-1],g[N-1]);
}
};
粉刷房子
dp【i】位置分三种状态,
分别时刷红,蓝,绿
用dp【i】【0,1,2】分别表示到第i位置时,刷红蓝绿的成本
class Solution {
public:
int minCost(vector<vector<int>>& costs) {
int n=costs.size();
vector<vector<int>>dp(n,vector<int>(3));
dp[0][0]=costs[0][0];
dp[0][1]=costs[0][1];
dp[0][2]=costs[0][2];
for(int i=1;i<n;i++)
{
dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i][0];
dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i][1];
dp[i][2]=min(dp[i-1][1],dp[i-1][0])+costs[i][2];
}
return min(dp[n-1][2],min(dp[n-1][0],dp[n-1][1]));
}
};
买卖股票的最佳时机含冷冻期
状态转移:
状态机:可买(可交易)dp【0】,可卖(买入)dp【1】,冷冻期dp【2】
dp【i】【0】=max(dp【i-1】【0】,dp【i-1】【2】)
dp【i】【1】=max(dp【i-1】【0】-costs【i】,dp【i-1】【1】)
dp【i】【2】=dp【i-1】【1】+costs【i】
初始化
dp【0】【0】=0
dp【0】【1】=-cost【0】
dp【0】【2】=0
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>>dp(n,vector<int>(3));
dp[0][1]=-prices[0];
for(int i=1;i<n;i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][2]);
dp[i][1]=max(dp[i-1][0]-prices[i],dp[i-1][1]);
dp[i][2]=dp[i-1][1]+prices[i];
}
return max(dp[n-1][0],dp[n-1][2]);
}
};
买卖股票的最佳时机含手续费
dp【i】【0】可买
dp【i】【1】可卖
dp【i】【0】=max(dp【i-1】【0】,dp【i-1】【1】+prices【i】-fee)
dp【i】【1】=max(dp【i-1】【1】,dp【i-1】【0】-prices【i】)
dp【0】【0】=0
dp【0】【1】=-prices【0】
class Solution {
public:
int maxProfit(vector<int>& prices, int fee)
{
int n=prices.size();
vector<vector<int>>dp(n,vector<int>(3));
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]-fee);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[n-1][0];
}
};
122. 买卖股票的最佳时机 II
dp【i】【0】可买
dp【i】【1】可卖
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】)
dp【0】【0】=0
dp【0】【1】=-prices【0】
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n=prices.size();
vector<vector<int>>dp(n,vector<int>(2));
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][0]-prices[i],dp[i-1][1]);
}
return dp[n-1][0];
}
};
53. 最大子数组和
dp【i】:以i位置为结尾的所有子数组最大和
fen为长度为1和不为1的
dp【i】=max(dp【i-1】+nums【i】,nums【i】)
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n=nums.size();
vector<int>dp(n);
dp[0]=nums[0];
for(int i=1;i<n;i++)
{
dp[i]=max(dp[i-1]+nums[i],nums[i]);
}
int ret=INT_MIN;
for(int i=0;i<n;i++)
{
ret=max(ret,dp[i]);
}
return ret;
}
};
918. 环形子数组的最大和
借鉴环形打家劫舍
转化为非环形
f【i】:以i位置为结尾的所有子数组最大和
g【i】:以i位置为结尾的所有子数组最小和
返回 f【i】和sum-g【i】中最大的
注意:当全为负数时 f【i】为负
sum-g【i】为0,会返回0 这是错误的结果
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n=nums.size();
vector<int>f(n);
vector<int>g(n);
f[0]=nums[0];
g[0]=nums[0];
int sum=0;
for(int i=1;i<n;i++)
{
f[i]=max(f[i-1]+nums[i],nums[i]);
g[i]=min(g[i-1]+nums[i],nums[i]);
}
for(auto v:nums)
{
sum+=v;
}
int Max=f[0];
for(int i=1;i<n;i++)
{
if(i==n-1&&sum-g[i]==0)
{
if(Max<f[i])
Max=f[i];
break;
}
if(Max<f[i])
Max=f[i];
if(Max<(sum-g[i]))
Max=sum-g[i];
}
return Max;
}
};
乘积最大子数组
乘积最大子数组
f【i】以i为结尾的乘积最大的子数组
g【i】以i为结尾的乘积最小的子数组
f【i】=
1.nums【i】
2.
乘积要分类 nums【i】》0,要与f【i-1】相乘
nums【i】《0,要与g【i-1】相乘
f【i】=max(nums【i】,f【i-1】*nums【i】,g【i-1】*nums【i】)
g【i】=min(nums【i】,f【i-1】*nums【i】,g【i-1】*nums【i】)
class Solution {
public:
int maxProduct(vector<int>& nums) {
int n=nums.size();
vector<vector<int>>dp(n,vector<int>(2));
dp[0][0]=nums[0];
dp[0][1]=nums[0];
int Max=INT_MIN;
for(int i=1;i<n;i++)
{
dp[i][0]=max(dp[i-1][0]*nums[i],max(nums[i],dp[i-1][1]*nums[i]));
dp[i][1]=min(dp[i-1][1]*nums[i],min(nums[i],dp[i-1][0]*nums[i]));
}
for(int i=0;i<n;i++)
{
Max=Max>dp[i][0]?Max:dp[i][0];
}
return Max;
}
};
乘积为正数的最长子数组长度
f【i】以i为结尾乘积为正数的最长子数组长度
g【i】以i为结尾乘积为负数数的最长子数组长度
分类
1.nums【i】==0 f【i】==0
2.nums【i】<0
f【i】=g【i-1】+1
要单独判断f【i-1】=0
g【i】=f【i-1】+1
2.nums【i】>0
要单独判断g【i-1】=0
f【i】=f【i-1】+1
g【i】=g【i-1】+1
class Solution {
public:
int getMaxLen(vector<int>& nums) {
int n=nums.size();
vector<vector<int>>dp(n+1,vector<int>(2));
dp[0][0]=0;
dp[0][1]=-1;
int Max=0;
for(int i=1;i<=n;i++)
if(nums[i-1]==0)
dp[i][0]=dp[i][1]=0;
else if(nums[i-1]<0)
{
if(dp[i-1][1]==0)
dp[i][0]=0;
else
dp[i][0]=dp[i-1][1]+1;
dp[i][1]=dp[i-1][0]+1;
}
else
{
dp[i][0]=dp[i-1][0]+1;
if(dp[i-1][1]==0)
dp[i][1]=0;
else
dp[i][1]= dp[i-1][1]+1;
}
for(int i=1;i<=n;i++)
Max=Max>dp[i][0]?Max:dp[i][0];
return Max;
}
};
等差数列划分
dp【i】以i位置为结尾的能构成等差数列的个数
dp【i】
1.nums【i】+nums【i-2】=nums【i-1】*2
dp【i】=1+dp【i-1】
2
dp【i】=0
class Solution {
public:
int numberOfArithmeticSlices(vector<int>& nums) {
int n=nums.size();
vector<int>dp(n);
if(n==1||n==2)
return 0;
dp[0]=dp[1]=0;
for(int i=2;i<n;i++)
{
if(nums[i]+nums[i-2]==2*nums[i-1])
dp[i]=1+dp[i-1];
else
dp[i]=0;
}
int sum=0;
for(int i=0;i<n;i++)
{
sum+=dp[i];
}
return sum;
}
};
最长湍流子数组
f【i】表示以i为结尾的最长湍流子数组 且i位置为小
g【i】表示以i为结尾的最长湍流子数组 且i位置为大
f【i】=g【i】=1
if(nums【i-1】> nums【i】)
f【i】=1+g【i-1】
else if (nums【i-1】<nums【i】)
g【i】=1+f【i-1】
else
f【i】=g【i】=1
class Solution {
public:
int maxTurbulenceSize(vector<int>& arr) {
int n=arr.size();
vector<int>f(n);
vector<int>g(n);
f[0]=g[0]=1;
int Max=INT_MIN;
for(int i=1;i<n;i++)
{
if(arr[i-1]>arr[i])
{
f[i]=g[i-1]+1;
g[i]=1;
}
else if(arr[i-1]<arr[i])
{
g[i]=f[i-1]+1;
f[i]=1;
}
else
{
f[i]=g[i]=1;
}
}
for(int i=0;i<n;i++)
{
Max=Max>f[i]?Max:f[i];
Max=Max>g[i]?Max:g[i];
}
return Max;
}
};