天哪!LeetCode的每日一题终于不是树啦!前几天觉得题目类型有些重复,都是一看就会写的_(:з」∠)_ 开始了动态规划!字符串!!正是我薄弱的地方,希望接下来一个月都是这些能让我好好学习学习
这道题一眼就能看出来是我不会的dp。因为[红,黄,红]每一块的长度不固定,真的不知道该怎么设定它的状态,如何状态转移也毫无头绪,好像当前叶子和它的前一片叶子关系不大。
我们试着按照动态规划的思路来分析一下:
一、找子问题
子问题必定是和叶子的颜色有关的。对于每一片叶子,有三种状态:在左边的红色区域、在中间的黄色区域、在右边的红色区域。还需要记录它的位置,我们选定从左往右推,所以子问题就是从第0片叶子到第i片叶子,第i片叶子处于某一状态时的最小操作数。
这道题由于它是一个全局的红黄红,所以一开始很难想到怎么分割成子问题。这里“子问题”的含义和递归、贪心非常不同,不是把问题分割成几块、每一块去寻找最优解、或是形式相同规模变小,而是你思考问题的时候,是怎样一步步得到最后结果的,是一个思考步骤上的“子”关系。
二、确定状态
由上述分析,设f[i][j]表示从第0片叶子到第i片叶子,第i片叶子为状态j时所需的最小操作数(j = 0,1,2)。
三、边界状态
考虑最先开始下手的第0片叶子,如果是红色,就不需要操作,f[0][0] = 0, 如果是黄色,f[0][0] = 1;
它不可能是状态1、2,由于这道题是取最小值,我们就把不可能的状态设为一个很大的数。这里要注意,我一开始用的是INT_MAX,但是它再+1就溢出了,因为leaves不超过50片,所以我直接INT_MAX / 2, 用INT_MAX的时候一定要小心。
再看第1片叶子,它不可能是状态2,也设为INT_MAX / 2。
四、状态转移
这个很简单就不用写了嘛
class Solution {
public:
int minimumOperations(string leaves) {
int size = leaves.length();
int f[size][3];
memset(f, 0, sizeof(f));
if (leaves[0] == 'y')
{
f[0][0] = 1;
}
f[0][1] = f[0][2] = f[1][2] = INT_MAX / 2;
for (int i = 1; i < size - 1; i++)
{
if (leaves[i] == 'y')
{
f[i][0] = f[i - 1][0] + 1;
f[i][1] = min(f[i - 1][0], f[i - 1][1]);
f[i][2] = min(f[i - 1][1], f[i - 1][2]) + 1;
}
else
{
f[i][0] = f[i - 1][0];
f[i][1] = min(f[i - 1][0], f[i - 1][1]) + 1;
f[i][2] = min(f[i - 1][1], f[i - 1][2]);
}
}
if (leaves[size - 1] == 'y')
{
f[size - 1][2] = min(f[size - 2][1], f[size - 2][2]) + 1;
}
else f[size - 1][2] = min(f[size - 2][1], f[size - 2][2]);
f[size - 1][0] = f[size - 1][1] = INT_MAX / 2;
return f[size - 1][2];
}
};
我才知道for循环里面定义的变量只能在for循环里面用_(:з」∠)_
And这道题是昨天中秋和国庆双节时候的题,看官方题解的评论要笑死了哈哈哈哈哈哈哈
十月这么快就到了,感觉会是能学很多知识的一个月呢。现在觉得听着喜欢的音乐学习真是太舒服了,已经变成我最喜欢做的事情了(*^▽^*)