动态规划
1.定义 ?
2.基本实现模式:
①先设置一个一维数组 int dp[n]/二维数组 int dp[n][n]
(什么时候设置一维数组/什么时候设置二维数组):取决于你的dp[i]具有什么意义 难点
做题总结:二维数组多涉及到位置关系,如坐标(i,j)用dp[i][j]表示
②找到dp中每一项与上一项或者下一项之间的关系:写出关系表达式 难点
③找到基准条件:如dp[0] dp[1] dp[2]
3.题型总结
first. House Robber 198 https://leetcode-cn.com/problems/house-robber/
官方题解
#include <algorithm>
class Solution {
public:
int rob(vector<int>& nums) {
int n=nums.size();//n houses
int dp[n+1];// dp[i] means visit the former ith house you can get the max money
if(n==1) return nums[0];
else if(n==2) return max(nums[0],nums[1]);
dp[0]=nums[0];
dp[1]=max(nums[0],nums[1]);
for(int i=2;i<n;i++)
dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
return dp[n-1];
}
};
官方题解中没有想到的点是:
dp[i]的意义是前i所房子所能得到的最大金额
所以等到最后一间房子,假设其为第k间房子的时候,就只有两种选择:要么是选择前k-1间房子所得到的最大金额,要么是选择前k-2间房子+第k间房子所得到的最大金额。
代码理解即为: max(dp[k-2],dp[k-3]+nums[k-1])
注意代码实现的时候,第k间房子对应的Index为k-1
MyCode
#include <algorithm>
class Solution {
public:
int rob(vector<int>& nums) {
int n=nums.size();//n houses
int dp[n+1];//dp[i] means visit the ith house you can get the max money
if(n==1) return nums[0];
else if(n==2) return max(nums[0],nums[1]);
dp[0]=nums[0];
dp[1]=nums[1];
dp[2]=dp[0]+nums[2];
for(int i=3;i<n;i++)
dp[i]=max(dp[i-2]+nums[i],dp[i-3]+nums[i]);
int ans=0;
for(int i=0;i<n;i++)
if(dp[i]>ans)
ans=dp[i];
return ans;
}
};
主要思路
当时构思的是dp[i]的意义是选择抢第i间房子时能得到的最大收益
所以每个房子都会有对应自己的dp[i],所以最后需要寻找dp[n]中的最大值才能得到答案。
second. Delete and Earn 740 https://leetcode-cn.com/problems/delete-and-earn/
官方题解
class Solution {
private:
int rob(vector<int> &nums) {
int size = nums.size();
int first = nums[0], second = max(nums[0], nums[1]);
for (int i = 2; i < size; i++) {
int temp = second;
second = max(first + nums[i], second);
first = temp;
}
return second;
}
public:
int deleteAndEarn(vector<int> &nums) {
int maxVal = 0;
for (int val : nums) {
maxVal = max(maxVal, val); //这里没看懂??
}
vector<int> sum(maxVal + 1); //创建一个sum数组
for (int val : nums) { //foreach语句,迭代数组全部元素
sum[val] += val; //给sum数组元素赋初值
}
return rob(sum);
}
};
官方题解主要思路
(部分语句没看懂)
MyCode
#include <algorithm>
class Solution {
public:
int deleteAndEarn(vector<int>& nums) {
vector<int> sum(1001,1);//1 - 1000 initial 1 only 1 means that the number of the number is 1
int n=nums.size();// 字符个数
if(n==1) return nums[0];
if(n==2)
{
if(nums[0]+1==nums[1] || nums[0]-1==nums[1])
return max(nums[0],nums[1]);
else return nums[0]+nums[1];
}
vector<int> norepeat(n);
vector<int> ::iterator it1=nums.begin();
vector<int> ::iterator it2=nums.end();
sort(it1,it2);
int t=0;
for(int i=1;i<n;i++)
{
if(nums[i]==nums[i-1])//前面跟后面对比
{
sum[nums[i]]++;
if(sum[nums[i]]==2) norepeat[t++]=nums[i];
}
if(sum[nums[i-1]]==1) norepeat[t++]=nums[i-1];
}
if(nums[n-1]!=nums[n-2]) norepeat[t++]=nums[n-1];
for(int i=0;i<t;i++)
cout << norepeat[i]<<" sum:"<<sum[norepeat[i]]<<endl;
int dp[n];//dp[i] measn 消除前i个可以得到的最大数
dp[0]=norepeat[0]*sum[norepeat[0]];
dp[1]=(norepeat[1]-1==norepeat[0])?max(norepeat[1]*sum[norepeat[1]],dp[0]):dp[0]+norepeat[1]*sum[norepeat[1]];
for(int i=2;i<norepeat.size();i++)
{
dp[i]=(norepeat[i]-1==norepeat[i-1])?max(norepeat[i]*sum[norepeat[i]]+dp[i-2],dp[i-1]):dp[i-1]+norepeat[i]*sum[norepeat[i]];
cout << dp[i]<<endl;
}
return dp[norepeat.size()-1];
}
};
主要思路
1.dp[i]的意义是选择前i个数字能够获得的最大收益
2.做这道题需要解决的问题是:
- 重复数字的解决–如[2,2,3,3,3] 轮到前3个如果单一考虑前i个数字能够获得的最大收益,则必然会抛弃第一个3而选择 第1 2 个已经组成的4
出错:片面思考
解决方法 (参考了官方题解) 设置一个数组sum,记录每个对应的nums[i]出现的次数,初始值为1,若出现多个则不断sum[nums[i]]++;最后比较的时候应该比较nums[i]*sum[nums[i]]而非单一的nums[i]
进一步探讨 同时我也选择构建一个新的数组norepeat企图消除相同重复的数字,但后来思考这里大概是可以优化的
理论上可以由第一个出现的重复数字决定了dp[i] (假设第一个出现的重复数字的Index为i) 则随后若有相同的即 if(nums[i+1]==nums[i]) dp[i+1]=dp[i];
【尚未尝试,之后会尝试优化一下】 - 乱序问题:因为题目讨论的是i-1 与i+1 即为相邻位置,然而案例给出的数据全都是乱序,所以想到了用sort先行对nums数组进行排序
与第一道题目的相似之处
1.同样是存在邻居困境问题,题目是这个意思,但第二道题需要进行处理一下才能转化为第一道题
待解决:10.12.21