动态规划的重点是:1)找状态转移方程;2)计算边界情况。
切入点:具体要用什么数据结构记录,该数据结构一般是数组或者数组变形,那么数组的每个元素代表什么,元素和元素之间具有什么联系。
推荐刷:5、45、1014、91;
70:a[n] = a[n-1]+a[n-2]; a[1] = 1,a[2] = 2;
98:a[n] = max(a[n-2] + nums[n],a[n-1]),即每次计算都尽量把原来较大的部分保留。
120:
arr[i][0] = arr[i-1][0]+triangle[i][0];
arr[i][j] = min(arr[i-1][j-1],arr[i-1][j]) + triangle[i][j];
arr[i][i] = arr[i-1][i-1] + triangle[i][i];
746:状态转移方程中a[i]代表跳到第i个位置目前所需花费的最小cost。因此,a[0]=a[1]=1;a[i]=min(a[i-2]+cost[i-2] ,a[i-1]+cost[i-1]),最终结果找a[n],n=cost.size()
213:198的变形。队列拆分成两个数组即可。0~n-1和1~n。转移方程:a[i]=max(a[i-2]+nums[i],a[i-1);边界:a[0] = nums[0],a[1]=max(nums[0],nums[1])
55:每个nums[i]代表从第i个 位置开始,可以往后走若干个位置。用一个数组array代表每个下标的可达与否,从前往后遍历nums数组,依次记录每个位置的可达位置。位置不可达时return。
45:这个是55的变形。55在意的是能不能到,因此array[i]存储1和0就可。而45在意的是最小,因此array[i]存储步数。
array[1:size - 1] = -1,array[0] = 0.
for i in nums
t = nums[i]
for k from i+1 to t+i &&k <size
if array[k] = -1
array[k] = array[i]+1
else array[k] = min(array[k],array[i]+1)
return array[size-1]
1014:拆成两部分,result= value[i]+i和value[j]-j
数组记录:以j为终点的result最大值;同时记录到j为止,max(value[i]+i)(i∈[0,j])
即递推公式为: result = max (result,maxnu + value[j] - j)
maxnu = max(maxnu,value[j]+j)
初始值:maxnu = value[0]
740:用数组记录每个nums[i]有多少个。而后,问题转化为打家劫舍198
状态转移方程为array[i] = max{array[i-1],array[i-2]+array[i]*i};初始数组每个元素记录i有若干个,结束时数组每个元素记录当前位置最多得到多少点数。
152:仍然采用max(array[i-1],array[i-1]*nums[i]),但考虑到有负数,应该简化成:
if(nums[i] < 0) swap(maxnum,minnum);
result = max(maxnum,minnum)
maxnum = max(maxnum,maxnu*nums[i])
minnum = min(minnnum, minnum*nums[i])
即,每次遇到负数,max和min将有交换,小的变大,大的变小。
是152的变形;
if(number = 0) positive=0,negative = 0
else if(number < 0) swap(positive,negative);negative++; if(positive>0) positive++;
else positive++;if(negative>0)negative++;
lt5是一道非常经典的动态规划算法。用数组array记录index为i~j的子字符串是否为回文数组。
即 array[i][i] = 1………………………(1)
array[i][j] = array[i+1][j-1] &(s[i] = s[j])……(2)
而回文字符串应该是从小到大,即从length = 1 到length = size, 即如果一个子串是非回文串,那其更长的字符串也不会是回文串
array[i][i] = 1
for i = 0 to n
for LEN = 2 to n
j = i + LEN - 1
if(s[i]=s[j])
if LEN <= 3 array[i][j] = 1
else array[i][j] = array[i+1][j-1]
else array[i][j] = 0
record MAX_LEN
return substr(i,MAX_LEN)
array[0] = 1,array[1] = 1;
s[i]的方程为:
if(s[i]!='0') array[i+1] += array[i]
if(s[i-1] !='0'&s[i-1]*10+s[i]<=26) array[i+1]+=array[i-1]
动态规划里, array存储到目前index为止,有若干种存储方式。
1:用动态规划,找max不重叠子区间
sort(nums)//根据区间起点从小到大排序
for(i = 1 : n)
for(j = 0 : i)
if(nums[j][1]<nums[i][0])
f[i] = max(f[i],f[j]+1)
return n - f[n-1]
2:用贪心
sort(nums)//根据区间终点从小到大排序
for auto e : nums
if(e[0]>end)//如果当前区间已经有覆盖,就result++
result++;
else
end = e[1]
return result;//
array[i][j]=array[i-1][j]+array[i][j-1]
注意由于只能右或者下,因此对这个二维数组实现的方法是:先写完边界情况(横纵轴只要1个为0就值为1),然后再按常规循环遍历。
a++;
if(a> array[i]) array[i] = a;
array[i]代表:以i作为终点的序列,有多少个递增的。
array[i] = max(array[j]) + 1 …… (j < i,当nums[i]>nums[j]时)
= 1 ……(当nums[i]是目前最小值)
O(n2)算法 for(i = 0 : nums_size)
for(j = 0 : i)
if(nums[i]>nums[j])
if(num[j]+1>nums[i]) nums[i] = max(nums[j]+1,nums[i]);
数组array[amout+1],每个index代表硬币和为index最少需要多少个硬币,0代表硬币组无法组合出当前结果。
for(auto i:coins) array[i] = 1;
for(j = min(coins) : amount)
if(array[j] != 0)
for(auto i :coins)
if(i + j <= amount)
temp=array[j]+1
if(array[i+j]==0)
array[i+j]=temp
else array[i+j] = min(array[i+j],array[j]+1);
return array[amout] == 0? -1 : array[i+j];
(583是1143的变形)
和标准答案给的解法略微不同
array[i][j]代表到text1[i]和到text2[j]为止,有多长的公共子序列
边界:即i = 0 || j = 0时,array[i][j] = array[i-1][0] | text1[i]==test2[j];//也可能是array[0][j-1]
for(i = 1: len1)
for(j = 1:len2)
if(text1[i]==text2[j]) array[i][j] = array[i-1][j-1]
else array[i][j] = max(array[i-1][j],array[i][j-1])
if(text1[i-1] == text2[j-1]) array[i][j] = min(array[i-1][j-1],array[i-1][j]+1,array[i][j-1]+1);
else array[i][j] = 1+min(array[i-1][j-1],array[i-1][j],array[i][j-1]);
lt34:
dp记录只允许k个节点不相邻时,可获得的最大价值
dp[i] = max(dp[i],dp[i]+dp[i - j - 1]+root->val),而求解dp需要用到dfs
先判断不可能的情况:例如数组和为奇数、数组的最大值>sum/2、数组长度<2。
这里的array[i][j]代表,0~i的index,值为j是否可以取得到。
array[i][j] = array[i-1][j] | array[i-1][j-nums[i]];
968有点难以理解,对每个节点r,有3种状态:
1、a状态:如果r必须放摄像头,cover整颗树需要的摄像头数目
2、b状态:无论r是否放摄像头,cover整棵树需要的摄像头数目
3、c状态:cover r的左右子树需要的摄像头数目,无论r是否能被覆盖
定义status {a,b,c}为root的状态,左右孩子的状态分别为{la,lb,lc}和{ra,rb,rc}的状态
a = lc + rc + 1;//左子树覆盖+右子树覆盖+r本身覆盖
b = min(a,min(la+rc,lc+ra))
c = min(a,lb+rb)
status dfs(TreeNode* root)
if(!root) return [INT_MAX,0,0]
auto [la,lb,lc] = dfs(root->left)
auto [ra,rb,rc] = dfs(root->right)
int a = lc+rc+1
int b = min(a,min(la+rc,lc+ra))
int c = min(a,lb+rb)
return [a,b,c]
array[size][size][2]
其中array[i][i][0]代表当前value为0的条件是否为真;
for (i=0;i<size;i+=2) array[i][i][0]=tmp - (s[i]-'0'),array[i][i][1] = tmp-(s[i]-'1')
for(int step=0;step < size; step += 2)
for(int i = 0; i + step< size;i+=2)
for(int j = i+1; j < i+step;j+=2)//j指的是&|^这些符号,以j点间隔开
left0 = array[i][j-1][0],left1 = array[i][j-1][1]
right0 = array[j+1][i+step][0],right1 = array[j+1][i+step][1]
if(j == '&')
array[i][i+step][0] = left0*right0+left1*right0+left0*right1;
array[i][i+step][1] = let1*right1;
else if(j == '|)
array[i][i+step][0] = left0*right0
array[i][i+step][1] = left0*right1+left1*right0+left1*right1
else if (j == '^')
array[i][i+step][0] = left0*right0+left1*right1;
array[i][i+step][1] = left1*right0+left0*right1
return array[0][size-1][result]
转移方程:
if matrix[i][j] == 1 temp[i][j] = 0
else temp[i][j] = temp[i-1][j] + temp[i][j-1]
注意边界情况:如果第一行或者第一列中有石头,则第这个位置之后的这行or这列的位置的temp值为0
lt264:丑数
很容易想到用一个数组存放之前的所有丑数,但要注意用三个指针记录当前取2、3、5的值的位置。
动态方程:value = min(array[p2] * 2,array[p3] * 3,array[p5] * 5);
if (array[pi]*i == array.end() ) pi++;