动态规划问题(小思)

对于解可能是指数形式,并且可供选择的策略不是很多,每一个策略选择后都会产生一种状态转移,可采用dp来求解
Top-down DP: 也就是用递归加上缓存,从上往下构建
Bottom-up DP: 就是利用纯数组,从下往上构建

买股票的5个问题

121. Best Time to Buy and Sell Stock

思路: 出现最小更新最小,否则更新最大利润。只进行第一次操作(买和卖)

122. Best Time to Buy and Sell Stock II

不限制操作次数
思路:1.贪心问题,如果后面比前面大,就把差值加上
2.找到波谷波峰,波谷买入,波峰卖出

309. Best Time to Buy and Sell Stock with Cooldown

思路:动态规划,给定三个状态,对值进行确定。画出三个状态以及状态之间的转移图即可得出状态转移方程。这个是简单的一维动态规划。

123. Best Time to Buy and Sell Stock III

要求:给定输入只能有两次交易,得到最大金额,更加泛华的解法看下一题,有k次交易的机会

188. Best Time to Buy and Sell Stock IV

要求:只有k完整交易机会,只有买和卖两种状态,然后构建两个二维数组,第二个二维数组的长度为k+1
ac:trick 1.只有在sell时才是一次完整的交易,
2.初始化时候buy[i][0] 和 buy[0][k] 的赋值,
3.sell[i][j] 的赋值
到此,买股票问题全部解决

72. Edit Distance

要求:求两个字符串的编辑距离,增加删除修改
思路:可以采用二维或者一维的进行求解
ac:trick,一维的时候,j在外围,pre = dp[0],这时候的pre指的是dp[i - 1][j - 1];

139. Word Break 不论是wordbreak还是coinchange都要判断子集是否可取

要求:给定字符串,给定词典,检查能否被完全拆解
思路:dp,一维,当计算到i的时候,判断 dp[j]以及j到i字符串是否在词典中

140. Word Break II

要求:同上,输出有用的结果
思路:dp,这里的dp的形式表现为dfs的时候进行的缓存,这里采取的策略是从前向后
trick : 当对s进行前缀划分的时候,对wordDict进行遍历,而不是对从
for (String word : wordDict) {
if (s.startsWith(word))
此处返回的是加了空格的字符串组成的list,此时返回的是一个list。
针对两个list的情况,可以看下backtracking

343. Integer Break

要求:给定一个数,分成几个数的和,得到这几个数的乘法值最大
思路:dp, 与wordbreak类似,更新的时候更新max

312. Burst Balloons

要求:给定输的数组,代表气球,其中引爆一个气球得到的奖励为nums[i - 1]*nums[i]*nums[i + 1]
思路:Then another interesting idea come up. Which is quite often seen in dp problem analysis. That is reverse thinking. Like I said the coins you get for a balloon does not depend on the balloons already burst. Therefore
instead of divide the problem by the first balloon to burst, we divide the problem by the last balloon to burst.
考虑的是给定左右边界,从最后一个位置引爆气球,然后可以区分成左右两个部分,因为我这块卡住的是两段,也就是两个可以认为是1 的数,两段最后引爆,
1.采用递归加缓存,第二种形式的动态规划
2.直接数组动态规划
ac:trick,在使用dfs的时候, ans = Math.max(ans, temp[le] * temp[i] * temp[ri] + burst(temp, le, i, memo) + burst(temp, i, ri, memo));因为两头的爆炸点不可取
trick:在使用纯动态规划的时候,一定要先把长度放在外围,这样炸起来是从对角线一层层往右上角进行炸的 注意思考转移方程

546. Remove Boxes

要求:消消乐
思路:建立一个三维memo进行缓存,memo{i}{j}{k} 其中k表示右侧有多少个和当前右边界相等的盒子的个数

32Longest Valid Parentheses

要求:最长的括号匹配长度
思路:可以采用stack来做,也可以采用dp的思路
trick:stack存的是下标,(入栈,)判断,dp,(不处理,)处理

10 Regular Expression Matching

trick: dp[i + 1][j + 1] = dp[i + 1][j - 1] || dp[i][j + 1];// 先判断,第一个表示删除 第二个表示正常加入,也就是匹配了第i个字符,然后考虑当前匹配第i -1 个子串

354 Russian Doll Envelopes

要求:俄罗斯套娃
思路:先按照长度进行升序排列,然后针对宽度进行最长递增子序列的解法

514. Freedom Trail

要求:如同打电话一样,转盘拨号码
ac:dfs+ memo可以解决
trick:纯dp时间复杂度有个M*N*N的,从当前m - 1个开始一直到0,把n的每个位置到当前元素的最小值都记录下来,遍历k遍历0到n找到m,min路径

44. Wildcard Matching

要求:通配符表达式
思路:dp,
trick:当出现 dp[i + 1][j + 1] = dp[i + 1][j] || dp[i][j + 1]; *号可要可不要,注意初始化

给定一个二维矩阵,求最大的子矩阵的和以及各个维度的上下标,可以可以降到3维,如果限定某个值,可以用TreeSet
363. Max Sum of Rectangle No Larger Than K

要求:给定一个矩阵,找出和不大于k的子矩阵和的最大值
思路:无
ac:利用到了二分的想法,以及行累加和的dp的思想,从i开始,从j到i,累加计算array(k),在用到k的时候,同时也更新了val,利用二分进行不断的比较

路径问题

174. Dungeon Game

要求:给定一个数组,要求从左上角到右下角,每一步可以掉血,也可以得到血,问从开始的位置,到结束的位置,起始位置最少携带多少血
思路:状态转移方程,计算出need,need = min (向右或者向下)- du[i][j],如果need小于0,那么就给到1
trick:dp[i][j] 要结合 du[i][j] ,以及下一步状态的变化

62. Unique Paths

要求:给定地图,可以向右,也可以向下,求问多少种路径
思路:最简单的dp

63. Unique Paths II

要求:有点不可达,求结果
思路:初始化, 加判断

64. Minimum Path Sum

要求:路径有权值,得到最小
思路:直接最小dp即可

131. Palindrome Partitioning

要求:输出一个字符串的合理拆分,保证每一个list集合里面的字符串都是回文的
思路:backtrack,定义的两个list,一个表示结果,可以remove掉最后一个
trick:注意删除最后一个元素, 回文的时候,传入的是起始和结束地址,省去了substring的操作

132. Palindrome Partitioning II

要求:给定输入的字符串,判断至少多少刀可以切分,最优解dp
思路:无思路
ac:如果j,i是回文那么以第i个结尾的切的刀数为cur[j - 1] + 1,dp的时候进行了回文的判定,非常巧秒的方法
trick:for (int i = 0; i < len; i++) {
int min = i;
for (int j = 0; j <= i; j++) {
if (arr[j] == arr[i] && (j + 1 > i - 1 || Palindrome[j + 1][i - 1])) {
Palindrome[j][i] = true;
min = j == 0 ? 0 : Math.min(min, cur[j - 1] + 1);
}
}
cur[i] = min;
}

516. Longest Palindromic Subsequence,注意对比substring

非常典型的一道dp问题,dp二维数组问题(拯救公主),dp通过考试问题,dp一条长度不断缩小问题(引爆气球,消消乐),dp最长递增子序列(子序列问题),dp两个字符串匹配问题(正则表达式,通配符,编辑距离),dp正方形计算面积(矩形采用的是stack,计算直方图面积的方法), dp
dp又有两种形式,一个是从上到下的dfs+memo,另外一种是数组,从下往上, dp

    public int longestPalindromeSubseq(String s) {
        return  dfsHelp(s, 0, s.length() - 1, new int[s.length()][s.length()]);
    }
    public int dfsHelp(String s, int i, int j, int[][] memo) {
        if (i == j) return 1;
        if (i > j) return 0;
        if (memo[i][j] > 0) return memo[i][j];
        int temp = Integer.MIN_VALUE;
        if (s.charAt(i) == s.charAt(j)) {
            temp= Math.max(dfsHelp(s, i+ 1, j - 1, memo) + 2, temp);
        } else {
            temp = Math.max(dfsHelp(s, i + 1, j, memo), dfsHelp(s, i, j - 1, memo));
        }
        memo[i][j] = temp;
        return temp;
    }
        public int longestPalindromeSubseq(String s) {
        if (s == null || s.length() < 1) return 0;
        int n = s.length();
        int[][] dp = new int [n][n];

        for (int i = n - 1; i >= 0; i--) {
            dp[i][i] = 1;// 此处很关键,
            for (int j = i + 1; j < n; j++) {
                if (s.charAt(i) == s.charAt(j)) {
                    dp[i][j] =  dp[i + 1][j - 1] + 2;               
                } else {
                    dp[i][j] = Math.max(dp[i + 1][j] , dp[i][j - 1]);
                } 
            }
        }
        return dp[0][n - 1];
    }
407. Trapping Rain Water II

trick:四个方向都走一遍
可以利用数组进行定义 : int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

402. Remove K Digits

要求:输入一个字符串是,删除k个数,求得删除后得到的值最小
思路:利用贪心的方法,当stk里面的最后一个比当前循环中的数大, 删除当前的数
trick:注意leading 0,以及下标值为top - 1.

321. Create Maximum Number 贪心

要求:给定两个数组,以及目标长度k,得到可以组成最大的数组,数组长度为k
思路:遍历,第一个数组取的数i从0 或者 k - len2开始,第二个数组中取得k - i, 分别选取子数组,组合,比较大小
trick:核心代码

  for (int i = 0; i < nums.length; i++) {
           while (j > 0 && resu[j - 1] < nums[i] && n - i  + j > k) j--;
           if (j < k) resu[j++] = nums[i];
       }
403. Frog Jump

要求:给定组石头的位置吗,下次跳的距离只能是本次跳k的 k-1或者k或者k+1
思路:先用hashset缓存下石头的位置,然后dfs

410. Split Array Largest Sum

要求:数组划分,给定一个数m,将数组分成m份,令m份中和最大的一份,数据最小
思路:无思路
ac:二分,目标值target是属于max到sum之间的,如果用target作为目标,如果可以正确分,分的分数不大于m,减少target,否则增加tar

517. Super Washing Machines

要求:给定一排洗衣机,保证每一台洗衣机洗衣服数目一样
思路:数学或者dp;
ac:数学,trick
machines: [0,3,0]; gain/lose array: [-1,2,-1]; max = 2, cnt = 0, -1, 1, 0, its abs peak is 1. So result is 2.
cnt累加

115. Distinct Subsequences

要求:两个字符串,输出一个子串组成另外一个子串,多少种情况
思路:初始化的时候注意1的问题
ac:当最后一个字符串相等的时候,可选可不选

97. Interleaving String

要求:两个字符串能否组成第三个字符串,
思路:谁少谁跟进

446. Arithmetic Slices II - Subsequence

思路:这题不会,过了

413. Arithmetic Slices

要求:给定一个输出
思路:可以采用dp来解, d

87. Scramble String

要求:检查两个字符串是否是Scamble输出
思路:递归求解

392. Is Subsequence

要求:判断s是不是t的子串
思路:对s的每一个字符采用indexOf函数,得到pos,更新pos
ac:indexOf,trick,这个比双指针要快

466. Count The Repetitions

要求:这个比较繁琐,可以双指针,暴力求解

128. Longest Consecutive Sequence

要求:给定一个数组,得到最长的无顺序版本的递增子集合,[400, 1,2,3]
思路:用一个set进行缓存,很easy的想法,可以用并查集来做。

472. Concatenated Words

要求:给定一个字符串数组,输出其中可以被子串组成的字符串的链表集合
思路: 两种1.利用dp来做,先进行升序排列,然后用wordbreak
2.利用单词查找树,对单词进行递归查询,

//核心代码
    public boolean dfsHelper (char[] arrs, TrieNode temp, int index, int count){
        TrieNode cur = temp;//because it must before 
        for (int i = index; i < arrs.length; i++) {
            int id = arrs[i] - 'a';
            if (cur.children[id] == null) {
                return false;
            }
            cur = cur.children[id];
            if (cur.isWord) {
                if (i == arrs.length - 1) {
                    return count >= 1;
                } 
                if (dfsHelper(arrs, temp, i + 1, count + 1)) {
                    return true;
                }
            }
        }
        return false;
    }

抢劫房子的几个问题

198. House Robber

要求:不能连续抢劫两家,问最大的抢劫金额
思路:每次抢劫的时候就看下, dp[i - 2] + nums[i] 是否大于dp[i - 1]
另外一个想法是第i个抢没抢,如果抢了,这样可以采用O(n)的时间复杂度
for (int i = 1; i <= num.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = num[i - 1] + dp[i - 1][0];
}

213. House Robber II

要求:数组是循环数组
思路:这个需要用到两个dp,因为是循环的,从开始选取两个下标连续的数,用来不抢,即可打破这个圆形
错误: 在进行循环时忘记更新下标 0 到length - 1 以及 1到 length- 2

337. House Robber III

要求:房子是二叉树,不能抢劫直接连着的
思路:dfs,返回的是一个数组

抢房子结束

85. Maximal Rectangle

要求:给定0和1 组成的数组,得到最大的连续1的面积
思路:跟解决直方图的那个一样, 利用stack,如果大,就就入栈,否则,一直计算面积
trick: h = i == len ? 0 : height[i]; 高度的数组要比列数的长度多一个
每次更新的时候,都要新的stack,之前的不要,因为

221. Maximal Square

思路:正方形这个就简单多了,
ac:采用dp,如果是0,为0,不是0,就取三个方向最小的

304. Range Sum Query 2D - Immutable

要求:给定矩阵,O(1)的时间复杂度,得到局部区域的累加和,并返回
思路:新建矩阵,利用左上方的三个值,计算当前的值, 类似上题

338. Counting Bits

要求:o[n]的时间复杂度,得到从1开始所有数的1个数
思路:dp[i]等于dp[i >> 1] + i & 0x01;

丑数

263. Ugly Number

要求:判断一个数是不是丑数,因子只包含 2, 3, 5.就是丑数
思路:如果求余得0,整除,最后看能不能得到1

264. Ugly Number II

要求:输出第n个丑数,
思路:两种方式,一个是直接用数组,加下标, 另外一个是3个队列

313. Super Ugly Number

要求:给定primes, 不再是 2, 3, 5,输出第n个
思路:一样的方式,只不过在计算每一个值的时候,
错误:没有初始的时候赋给最大值
丑数结束

357. Count Numbers with Unique Digits

要求:给定n表示最大为10的n次方,统计有多少个各个位数都不相同的数,
思路;n每增加一个,可选数字就减少一个

加减乘除操作

494. Target Sum

要求:给定数组,得到加减操作,确定多少种方案,可以得到目标值
思路:dfs,+ memo ,另外一种
有一个相当牛逼的解法
存dp,先计算,改成存加法,然后参考416

416. Partition Equal Subset Sum

要求:判断一个数组,是否可以被均分为相等的两个部分
思路;dp,这个dp说参考了01背包问题,太厉害了,dp[0] = true,
核心代码:

 for (int num : nums) {
        for (int i = sum; i > 0; i--) {
            if (i >= num) {
                dp[i] = dp[i] || dp[i-num];
            }
        }
    }
368. Largest Divisible Subset

要求:输出最长的可以整除子集合
思路:不涉及到局部有序,可以直接排序,在更新count的时候,可以同时保存pre 时间复杂度为n的平方

279. Perfect Squares 因为这个里面有1,所以所有的都是组成的, 而wordbreak以及coinschange就不一样了

要求:给定一个数,最少有多少个完全平方数组成
错误:输出超时, 对i进行处理,j得到是平方增加的

  public int numSquares(int n) {
        if (n < 1) return 0;
        int[] dp = new int[n + 1];
        Arrays.fill(dp, Integer.MAX_VALUE);
        dp[0] = 0;
        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);
            }
        }
        return dp[n];
152. Maximum Product Subarray

要求:给定数组,输出子数组中乘法得到的最大的结果
思路:每次乘法,都保存最大值以及最小值

91. Decode Ways

要求:输出多少种编码方式
思路:从后往前,注意0直接continue

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值