此题按照常规逻辑应该是暴力解,即使做了剪枝,时间复杂度也会达到O(N*2)。
暴力解代码实现很简单,我们就不分析了。让我们直接进入DP。
DP思考路线
1.DP题当然先从DP特性入手。-- 最佳子结构 -- 无后效性。--重复子问题
容我赘述一下三个特性
- 最优子结构 指的是,问题的最优解包含子问题的最优解。
- 无后效性 有两层含义,第一层含义是,在推导后面阶段的状态的时候,我们只关心前面阶段的状态值,不关心这个状态是怎么一步一步推导出来的。第二层含义是,某阶段状态一旦确定,就不受之后阶段的决策影响。无后效性是一个非常“宽松”的要求。只要满足前面提到的动态规划问题模型,其实基本上都会满足无后效性。
- 重复子问题那就是,不同的决策序列,到达某个相同的阶段时,可能会产生重复的状态。
2.第二分析题目给的特殊玩意 (就是给的公式 或者 条件 反正这种都是DP的切入点!!!)
3.结合递推公式的特点和第二点分析公式
4.写出递推公式(这样就完成了80%了 剩下的就是处理细节和代码实现,不同的题有不同的代码细节我能力有限 只能浅谈这道题了。)
那我们就一步一步来
1.第一步比较硬性,就是单纯地快速回想这几个特性的在脑海里的概念,重复子问题一般都是从暴力解中了解,其余两个都是结合递推公式特点来联想。
最佳子结构 :回想平常了解的递推公式,都是以最优(最大or最小)存在的,所以 dp(N) 肯定是XXX的最优解。
无后效性 :回想我上篇打家劫舍特点分析 相邻,但我们只从单方向考虑。也就是,我们的思路应该是单调性的 一路顺下去的,就只考虑顺序or逆序(针对单数组问题),这也是符合无后效的特点。
2.这个题目的特点就是给出了条件(也就是公式)。(相当于就是 题目:“哥! 快!分析我下面那个 ,分析就可以A我了!就可以A了我了!!AAAAAA****我了!!”) 咳咳...
公式如下:
- values[i] + values[j] + i - j,
- dp(N) = XXX (这是我们常见的递推公式)
我相信大家最直观的就是 dp(N)= max(values[i] + values[j] + i - j,dp(N-1))
3.直观可不行 得需要具体逻辑!第三步!结合!
回想起来 常规思路我们可以设 dp(j) 是以J为结尾XXX的最大值(不包含N,0 ~ N-1);
难点就在我们如何把XXX表述出来! 所以就是结合!结合!结合! 结合题目给的特点--公式
- 发现 给出条件(i < j) 很清晰地知道 肯定是要给出以 j 为结尾 的递推公式。
- 将 公式移位发现 values[ i ] + i + values[ j ] - j (i < j),以 + 分成两部分看 考虑 以j 为结尾的最大值 只需要保证 values[ i ] + i是最大就行了。
所以递推公式显而易得 res= max(res, dp( j ) + values[ j ] - j);
这样递推公式也就和题目公式结合起来了。从上述有序列表 简单分析可得 dp[j] 只是在寻找 values[ i ] + i 的最大值即可。
代码如下:
class Solution {
public int maxScoreSightseeingPair(int[] values) {
int n = values.length;
int dp = 0, res = 0;
for (int i = 1; i < n; i++) {
dp = Math.max(dp, values[i - 1] + i - 1);
int j = i; //为了呼应我上面的公式 j只是方便理解。
res = Math.max(res, dp + values[j] - j);
}
return res;
}
}
PS:这是第二篇写的博客,如果有不好的地方 希望多多包含。
其实我觉得DP这玩意还是刷题,得先从经典题目入手,然后举一反三,这样子DP的感觉是越来越好的。