【LeetCode】动归
一维
70 爬楼梯E
198 小偷E
- 解法:
dp[i] = Math.max(dp[i-1], dp[i-2]+nums[i-1]);
413 等差数列划分M
- 题意:找出数组中等差数列的数量(最少为3个元素算一个等差数列)
- 解法:
- 差分:
for(int i = 2; i < n; ++i){
if(nums[i-1] - nums[i] == d){
t++;
}else{
d = nums[i-1] - nums[i];
t = 0;
}
ans += t;
- 滑动窗口:
for(int i = 2; i < n; ++i){
int d = nums[i] - nums[i-1];
if(d == pred){
L++; //滑动窗口增加
}else{
res += (L-1)*(L-2)/2; //规律
L = 2; //滑动窗口重新设为2
pred = d;
}
}
//对最后一组滑动窗口再计算一次
res += (L-1)*(L-2)/2;
- 动态规划:
for(int i = 2; i < n; ++i){
if(nums[i] - nums[i-1] == nums[i-1] - nums[i-2]){
dp[i] = dp[i-1] + 1; //等差数列相关的状态转移方程
res += dp[i];
}
}
二维
64 最小路径和M
- 题意:非负二维数组,从左上到右下角的最小路径和
- 解法:
-
动态规划:
状态转移方程:dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
但是i=0和j=0的时候要判断 -
动态规划(二维变一维):
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(i == 0 && j == 0){
dp[j] = grid[i][j];
}else if(i == 0){
dp[j] = dp[j-1] + grid[i][j];
}else if(j == 0){
dp[j] = dp[j] + grid[i][j];
}else{
dp[j] = Math.min(dp[j], dp[j-1]) + grid[i][j];
}
}
}
542 01矩阵M
- 题意:输出由01组成的矩阵每个元素与0的距离(0是0,1要看离它最近的0)
- 解法:
- bfs:让所有的0入队,遍历一圈找到离它们最近的1入队,并dist++,
//bfs
while(!queue.isEmpty()){
int[] cell = queue.poll();
int x = cell[0];
int y = cell[1];
for(int[] di : dirs){
int nx = x + di[0];
int ny = y + di[1];
if(nx >= 0 && ny >= 0 && nx < m && ny < n && !visited[nx][ny]){
if(mat[nx][ny] != 0){
dist[nx][ny] = dist[x][y] + 1;
visited[nx][ny] = true;
queue.offer(new int[]{nx, ny});
}
}
}
}
- 动态规划:从左上右上右下左下四种方向,设置移动方向,比较距离最小值。
//左上。即后来的dist值与左边和上边的dist值进行比较。
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
if(i-1 >= 0){
dist[i][j] = Math.min(dist[i][j], dist[i-1][j] + 1);
}
if(j-1 >= 0){
dist[i][j] = Math.min(dist[i][j], dist[i][j-1] + 1);
}
}
}
221 最大正方形M
- 题意:找出01矩阵中包含的最大的正方形,输出面积
- 思路:
- 暴力法:找到一个1就当做正方形的左上角,在剩余可围成的最大矩形中,先判断斜线上的元素是否为1,若为1,则判断该元素行和列上的元素是否为1,都为1则记录边长
//遍历到1将其作为正方形左上角
if(matrix[i][j] == '1'){
res = Math.max(res, 1);
//还剩下的最大矩阵边长reMain
int reMain = Math.min(m-i, n-j);
//遍历斜线
for(int k = 1; k < reMain; ++k){
boolean flag = true;
if(matrix[i+k][j+k] == '0'){
break;
}
//当斜线上的元素符合时,再检查该处行和列上的元素
for(int x = 0; x < k; ++x){
if(matrix[i+x][j+k] == '0' || matrix[i+k][j+x] == '0'){
flag = false;
break;
}
}
//每多在斜线上前进一格并判断成功,记录res
if(flag == true){
res = Math.max(k+1, res);
}else{
break;
}
- 动态规划:
for(int i = 0; i < m; ++i){
for(int j = 0; j < n; ++j){
//当前位置为‘0’
if(matrix[i][j] == '0'){
dp[i][j] = 0;
}else{
if(i == 0 || j == 0){
dp[i][j] = 1;
}else{
//状态转移方程
dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i-1][j-1]), dp[i][j-1]) + 1;
}
}
res = Math.max(dp[i][j], res);
}
分割类问题
279 完全平方数M
- 题意:等价于给定若干个数字,每个数字可以被使用无限次,求能凑出n使用的数字最少个数
- 没有限制数字只能用一次,所以是完全背包问题
- 动归:一维
for(int i = 1; i <= n; ++i){
for(int j = 1; j*j < i; ++j){
dp[i] = Math.min(dp[i], dp[i-j*j] + 1);
}
}
- 动归:二维完全背包
91 解码方法M(同剑指offer46)
- 题意:对任意数字组成的字符串解码为字母,求能组合出的方式最大有多少种
- 解法:难点在于为什么可以题意翻译成状态转移方程。
139 单词拆分M
- 题意:给定字符串和字典,判断字典中的单词能否组成字符串
for(int i = 0; i < n+1; i++){
for(int j = i; j >= 0 && j >= i-maxw; --j){ //剪枝
if(dp[j] && set.contains(s.substring(j, i))){
dp[i] = true;
break;
}
}
}
子序列问题
300 最长递增子序列M
- 题意:给定数组找出最长子序列
- 解法:dp[i]定义为必须选i元素时的最长子序列
- 动归
//状态转移方程
for
int dp[i] = 1;
for
if(nums[i] > nums[j])
dp[i] = Math.max(dp[i], dp[j]+1);
max = Math.max(max, dp[i]);
- 动归 + 二分
d[i]表示以i元素为当前数组最大元素的序列。遍历数组nums,每遇到一个比d[i]大的num,则入队。若小,则从d[i]中找到比num小的,把num放在该处的后一个位置上,更新d[i]
1143 最长公共子序列M
- 题意:求两字符串最长公共子序列,abcde 和ace的就是ace
- 解法:构建二维数组。分情况讨论
//当前元素相等时,后置dp的值为左上角dp的值+1
dp[i+1][j+1] = dp[i][j] + 1;
//当前元素不同时
dp[i+1][j+1] = Math.max(dp[i+1][j], dp[i][j+1]);
背包问题
416 分割等和子集M
- 题意:正整数数组,分割成两个和相等的子数组
- 解法:target = sum / 2, 转化为找数组中能否凑出恰好和为target的子数组的01背包问题
for(int i = 1; i < n; ++i){
for(int j = 1; j <= target; ++j){
if(nums[i] <= j){
dp[i][j] = dp[i-1][j] | dp[i-1][j-nums[i]];
}else{
dp[i][j] = dp[i-1][j];
}
}
}
474 一和零M
- 题意:二进制,字符串数组,找出恰好能凑出m个0,和n个1的子数组个数
- 解法:三层嵌套循环,逐个字符串动态规划,dp三维优化至二维
for(int i = 0; i < len; ++i){
for(int j = m; j >= count[i][0]; --j){
for(int k = n; k >= count[i][1]; --k){
dp[j][k] = Math.max(dp[j][k], 1 + dp[j-count[i][0]][k-count[i][1]]);
}
}
}
322 零钱兑换M
- 题意:完全背包问题,零钱不限,求凑出amount最少硬币数。
- 解法:完全背包,外层循环amount,内层遍历硬币
- 动归(记忆性递归):
//由于是求最小,所以可以将初始值设为最大,用Math.min求最小
for(int i = 1; i <= amount; ++i){
for(int j = 0; j < len; ++j){
if(coins[j] <= i){
dp[i] = Math.min(dp[i], 1+dp[i-coins[j]]);
}
}
- bfs:
//对每个零钱减一次,并入队,逐步搜索,一旦恰好减到0,则说明找到了最小值
while(!queue.isEmpty()){
int size = queue.size();
for(int i = 0; i < size; i++){
Integer head = queue.poll();
for(int j = 0; j < len; ++j){
int next = head - coins[j];
if(next == 0){
return step;
}
if(next < 0){
break;
}
if(!visited[next]){
queue.offer(next);
visited[next] = true;
}
}
}
step++;
字符串编辑
72 编辑距离M
- 两个字符串,插入、删除、替换执行最少次数,使两个字符串相等
- i,j指向各自位置,
//当两位置上字符相等时,
dp[i][j] = dp[i-1][j-1];
//当两位置上字符不相等时,
//1. 替换操作
dp[i][j] = dp[i-1][j-1]+1;
//2. 插入i位置或删除j位置
dp[i][j] = dp[i][j-1]+1;
//3. 插入j位置或删除i位置
dp[i][j] = dp[i-1][j]+1;
650 只有两个键的键盘M
//状态转移方程
dp[i] = Math.min(dp[i], dp[j] + dp[i/j]);
//(内层for循环可以将j < n 优化为j * j <= n)
10 正则表达式H
//状态转移方程
//当前字符相等,或j位置为.时
if(s.charAt(i) == p.charAt(j) || p.charAt(j) == '.') dp[i][j] = dp[i-1][j-1];
//当前j位置字符为*时
//1. j-1位置相等或为.时
if(s.charAt(i) == p.charAt(j-1) || p.charAt(j-1) == '.') dp[i][j] = dp[i-1][j] || dp[i][j-2];
//2. j-1位置不相等且不为.
else dp[i][j] = dp[i][j-2];
//都不符合上述情况
else dp[i][j] = false;
股票交易
121 最合适时间买卖股票E
常规法
动归法:
//当前天数结束时,不持股时手里的现金数
dp[0] = Math.max(dp[0], dp[1]+prices[i]); //昨天也不持股,昨天持股但是今天卖了的最大值
//当前天数结束时,持股时手里的现金数
dp[1] = Math.max(-prices[i], dp[1]); //第一次买入股,和昨天也持股且今天不卖的最大值
309 最佳买卖股票时期含冷冻期M
//第i天结束时手中不持有股票,且不在冷冻期的收益
dp[i][0] = Math.max(dp[i-1][2], dp[i-1][0]);
//第i天结束时手中持有股票的收益
dp[i][1] = Math.max(dp[i-1][0]-prices[i], dp[i-1][1]);
//第i天结束时手中不持有股票,且在冷冻期的收益
dp[i][2] = dp[i-1][1]+prices[i];
241 为运算表达式设计优先值M
//1. 遇到全数字的情况直接返回res
//2. 遍历字符串,遇到运算符,则对运算符左右两边的字符串再进行一次方法调用
for(int i = 0; i < expression.length(); ++i){
if(expression.charAt(i) == '+' || expression.charAt(i) == '-' || expression.charAt(i) == '*'){
List<Integer> left = new ArrayList<Integer>(diffWaysToCompute(expression.substring(0, i)));
List<Integer> right = new ArrayList<Integer>(diffWaysToCompute(expression.substring(i+1)));
for(int l : left){
for(int r : right){
switch(expression.charAt(i)){
case '+' :
res.add(l+r);
break;
case '-' :
res.add(l-r);
break;
case '*' :
res.add(l*r);
break;
}
}
}
}
}

浙公网安备 33010602011771号