这并不是一个难题,但是我确实在做题中得到了一些启发,所以记录一下
先讲一讲这个题目的做法:
首先不难想到这是一个dp问题,(由 i 可以跳到 j ) 而且应该不难, 要不然就不是medium了,doge 那么,暴力的dp就是:
dp[j] = max (dp[i] + nums OR dp[j] = dp[i] + nums - x) , i<j, 前面表示nums[i] nums[j] 是同奇偶,后面是nums[i] 和 nums[j]不同奇偶, 时间复杂度是O(n^2),对于 1e5的数据会超时
那么我们肯定是要优化的,如果做过那个最大数组元素连乘积,就会想到这就是一个dp+分类, 也就是说我们维护一下 上一个是奇数的最大值 和 上一个是偶数的最大值, (btw, 最大数组元素连乘积 就是维护一下 最大连乘积(正数) 和 最小连乘积(负数))
后面就没有什么需要赘述的了,还是要看一下数据量和返回值类型 选择long long
上代码: (我的代码,可以通过)
class Solution {
public:
long long maxScore(vector<int>& nums, int x) { //审题一开始在0 初始化有问题
long long lastOddMax=nums[0]%2?nums[0]:-x,lastEvenMax=nums[0]%2?-x:nums[0];
for(int i=1;i<nums.size();i++){
int num=nums[i];
if(num%2){// odd
lastOddMax=max({lastOddMax,lastOddMax+num,lastEvenMax+num-x});
}else{// even
lastEvenMax=max({lastEvenMax,lastEvenMax+num,lastOddMax+num-x});
}
}
return max(lastEvenMax,lastOddMax);
}
};
some thoughts:
1. 第一次提交的时候,是没有通过的,经过检验发现是题目要求一开始必须在0出发,而我的代码是不要求从0出发的,也就是初始化有问题,这里说一下,既然题目要求了第一次必须从下标0开始 所以就需要赋值成一个nums[0],一个-x, 类似的如果不要求第一个从哪里开始只是要求在奇偶性改变的时候 -x ,就应该赋值成一个nums[0],一个0,可以想一想为什么,这对于想通dp / dfs的初值设置是很有好处的
2. 第二个事情就是我定义的dp实际上表示的是 从奇数跳转过来的最大值 和 从偶数跳转过来的最大值, 他每次都是和自己比较了一下大小的,所以最后直接返回就是了,但是这是这个题目特殊的子问题的分割方式决定的,更常见的dp是dp[i]表示一定取到nums[i]下的最优值,之后用一个循环外的变量拿到最大值, 代码应该是像这样: 这里的lastOddMax,lastEvenMax就不必和自己比较了
for(...){
if(num%2){
lastOddMax=max(lastOddMax+num,lastEvenMax+num-x);
ans=max(lastOddMax,ans);
}else{
lastEvenMax=max(lastEvenMax+num,lastOddMax+num-x);
ans=max(lastEvenMax,ans);
}
}
重头戏来了:
做题顺序:
1. first 读一下题目,有一个大致的印象,是哪一个类别的(字符串,图,树),该类别一般都有什么算法
2. second 看一看数据量,O(n) O(nlogn) ... O(n^2) O(n^3)
3. third 如果感觉做过类似的题目, 那就往上面靠, 仿写
没有类似的题目,想一想一些通用的思想, dp, 贪心, sort, 递归/dfs, 二分法
3.1 使用dp 就要想好定义, 最好能写出来i,j的含义, 同时也可以看看分类(a) [:i][:j]
(b)dp[i]表示选择nums[i]最优 (c) num[i]另起炉灶
3.2 贪心 不用严格证明正确性,能够贪心一下简化一点思维就很好了,常见的可以是 sort + 贪心, 反悔贪心: 针对于需要等待积累 让量变引起质变的题目, 循环外记录ans
3.3 sort 可以看成是一种数据预处理or一种代价不大的贪心, 要求题目的选择是 非连续且不要求 相对位置,针对多目标最值或者多元素结构体,可以先固定一维最好或者最差,减少思维量
3.4 dfs/递归 对于树/图的一种普适法, 尤其对树有奇效,可以大大简化思维量,类似归纳法,只需要 边界条件(即n=0,n=1), 递推式,(f(n)=g(f(n-1)...f(1))) 使用记忆化后可以降低时间复杂度
3.5 二分法 针对于暴力超时,但是nlogn能过, 使用二分有个条件,就是ans左右两边一定是左边全0 右边全1或者左边全1右边全0,ans是第一个0或者第一个1
4. 优化时空 使用特殊的数据结构
unordered_map / map 用时间换空间 -> multimap / val取vector
unordered_set / set 插入删除logn, 会去重 -> multiset(不去重)
单调栈 单调队列
做题原则:
1. 不管方法好坏或者时空复杂度, 第一步要做的是解出来这个题, 只有先做出来,然后再去想时空的优化,在掌握了常见算法之后, 大部分优化都是数据结构的调整, 也有一些是思路的调整,这个就说明你的第一步定位没有定位好, 也是我们要通过刷题学习积累的feel.
2. 对于新的算法,特殊的数据结构,这个没有做出来是正常的,也不必花费时间死想,正确的姿势应该是-> 搞懂这个题, 搞懂这个模板, 刷五六道类似题目, 过一段时间回顾, 之后就自然掌握了,下一次他就应该进入你的知识库了