【努力刷力扣】第四十三天 --- 区间DP洛谷P1063能量项链+力扣344. 反转字符串
引言
本人初次尝试写博客,希望各位看官大佬多多包容
有错误希望巨巨们提出来,我一定会及时改正,谢谢大家
在自己最好的年纪,找到了未来的目标
还有1年奋斗刷题,明年去面试实习,加油!
题目一要求:
整体思路
为什么想到了DP?
这题乍一看,给个环,环上有珠子,问我咋捏爆珠子,才能获得最大效益,典型的,给个环(或者一个序列),找"最优"状态问题,完全可以上马DP。
(下方的分析就是区间DP常用的模板)
1、找状态
在一个环上区间DP,必备i,j表示区间开头和结尾,剩下的就什么都不需要表示了,综上,两个状态i,j
2、初始化
因为最开始只有一个珠子的时候没能量,所以是0.
3、找转移
<一定记住:大的区间最优是通过小区间最优转化+当此消耗而来。>
区间DP最常用模板:即枚举所有小区间,在所有小区间中找到最优再加上当前区间消耗
再回到本题,很明显,大区间i,j它能产生多少能量,完全取决于子区间,因为我们通过看题中的计算方法样例:
当我们合并i=4,j=2这个区间时,先41再2,和先12再4,自己算一下是明显不同的,再加之当前区间消耗也不是固定的(这个是该题重点),所以必须是小区间+选择了该小区间后大区间消耗,二者叠加产生最优值,我们才会选。
综上:dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + a[i] * a[k + 1] * a[j + 1]);就是状态转移方程,这里一定要注意的是,本区间消耗收到所选择的小区间影响,所以必须和小区间绑定相加。
(其实从这里也就看出来为啥这题不能贪心,就算是小区间你选择了最大利益化,但是可能加上本次的消耗后,综合起来的利益就未必最大了,可能有的小区间所获了利益并不太理想,但是一旦加上了当此产生利益反而大了,所以我们DP选择的是综合最大化)
4、找答案
因为是环状处理,所以在一个环上,从任意一个节点断开开始从前到后捏珠子产生的能量不同,所以必须每个段点产生的链都看一遍找最大的。
具体代码(内附注释)
1、因为这是一个环,所以采用了环状处理大法,将1到n-1再抄一遍,具体分析见我的另一篇博客有详解石子合并
2、大区间本次的消耗参考题中给的例子:
当我们合并i=4,j=2这个区间时,假设子区间是4和1,那么现在只需要将剩下两个珠子(因为41合并之后会留下一颗珠子,总共三颗珠子),我们看上方第二个乘法表达式,肯定有a[i]=10,k=1,a[k+1]=3,a[j+1]=5,所以表达式拼出来了。
3、遇到这种环或者一个序列上的DP问题时候,我们需要从小到大找区间,所以会有一个双层for循环:
外层控制步伐,里层控制从何处开始找,而这搭配枚举区间。
4、在上面那个双层for循环时候,如果是环,外层初始是2,序列的时候,初始是1。
#include <string>
#include <stack>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
#include <iostream>//头文件不用这么多,我是写别的时候附带着就写上去了
using namespace std;
int dp[550][550] = { 0 };
int a[550];
int n;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i + n] = a[i];
}
for (int len = 2; len <= n; len++) {//控制步伐大小
for (int i = 1, j; i + len <= 2*n; i++) {//控制区间起点,这样二者一配合就可以完美的枚举每个区间了
j = i + len - 1;
dp[i][j] = 0;
for (int k = i; k < j; k++) {
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j] + a[i] * a[k + 1] * a[j + 1]);//状态转移。
}
}
}
int Max = 0;
for (int i = 1; i <= n; i++) {
Max = max(Max, dp[i][i + n - 1]);//找答案
}
cout << Max;
}
(所有代码均已运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):
时间复杂度:O(n^2)
题目二要求:
做完了DP,实在是没精力了,就做了到签到题找找感觉,哈哈哈。
整体思路
超级简单,就是做出两个指针,一个i从头开始,一个(num-i-1)从尾部开始,交换二者数据, 交换完数据I++,num-i-1自然向前移动了,重复上述过程即可。
具体代码(内附注释)
class Solution {
public:
void reverseString(vector<char>& s) {
int num = s.size();
for (int i = 0; i < num / 2; i++) {
swap(s[i], s[num - 1 - i]);
}
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):