zzz
1.零钱兑换(322):求最值
一:确定状态:
)1:最终状态:要想达到用最少数量k的硬币凑出目标值n,则前面的k-1需要凑出n-ak,且k-1的数量一定是最少的
)2:子问题:最少用多少枚硬币可以凑出n-ak
二:转移方程: f(i)= min(f(i),f(i-coin(j))+1)
三:初始条件和边界条件:f(0) = 0
四:计算顺序:由下到上
/*
执行用时:15 ms, 在所有 Java 提交中击败了69.31%的用户
内存消耗:39.2 MB, 在所有 Java 提交中击败了5.77%的用户
*/
class Solution {
public int coinChange(int[] coins, int amount) {
if(amount==0) return 0;
//dp[i]:当amount为i时,凑成i的最小硬币使用数
int[] dp = new int[amount+1];
//初始值,可以的取值范围是[amount+1,Integer.MAX_VALUE)
Arrays.fill(dp,amount+1);
dp[0] = 0;
for (int i =1;i<=amount;i++){
for (int j = 0;j<coins.length;j++){
if(i-coins[j]<0) continue;
dp[i] = Math.min(dp[i],dp[i-coins[j]]+1);
}
}
return dp[amount]==amount+1?-1:dp[amount];
}
}
2.零钱兑换 II(518):求计数
一:确定状态:
)1:最终状态:凑成总值为n的方法数,等于凑成总值为n-coin[k](0<=k<=coin.length-1)的方法的总和。
)2:子问题:凑出n-coin[k]的方法数
二:转移方程:f(i)=f(i-coin[0])+...+f(i-coin[coin.length-1])
即f(i)= f(i)+f(i-coin[k])
三:初始条件和边界条件:当总金额为0时,方法数为1;没有硬币时,方法数为0
四:计算顺序:从下到上
/*
执行用时:3 ms, 在所有 Java 提交中击败了79.79%的用户
内存消耗:36.1 MB, 在所有 Java 提交中击败了65.24%的用户
*/
class Solution {
public int change(int amount, int[] coins) {
if(amount==0){
return 1;
}
if(coins.length==0){
return 0;
}
int []dp = new int[amount+1];
dp[0] = 1;
for(int j = 0;j<coins.length;j++){
for(int i = coins[j];i<=amount; i++){
dp[i] = dp[i]+dp[i-coins[j]];
}
}
return dp[amount];
}
}
3.不同路径(62):求计数问题
一:确定状态:
)1:最终状态:机器人在(m-1,n-1)的位置,那他的前一步一定在(m-1,n-2)或者(m-2,n-1)
)2:子问题:分别求出到达(m-1,n-2)和(m-2,n-1)的方法数,并相加
二:转移方程:f(i,j) = f(i-1,j)+f(i,j-1)
三:初始条件和边界条件:f(0,0)= 1 到原地有一种方式,即机器人不动即可
由于机器人只能往下走或者往右走,所以f(0,k)= 1 and f(k,0)= 1
四:计算顺序:从上到下,从左到右
/*
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.4 MB, 在所有 Java 提交中击败了54.32%的用户
*/
class Solution {
public int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
dp[0][0] = 1;
for(int i = 0;i<m; i++){
dp[i][0] = 1;
}
for(int j = 0; j<n;j++){
dp[0][j] = 1;
}
for(int i = 1;i<m;i++){
for(int j = 1;j<n;j++){
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
4.跳跃游戏(55):求存在性问题
一:确定状态:
)1:最终状态:青蛙到达最后的地方n,则青蛙首先能到达k,并且k+ak>=n
)2:子问题:青蛙是否能够到达k
二:转移方程:f(i) = f(j)&&(j+aj>=i) --(i>j)
三:初始条件和边界条件:f(0)=true,肯定能够到达原地
四:计算顺序:从下到上
/*
执行用时:416 ms, 在所有 Java 提交中击败了8.81%的用户
内存消耗:40.5 MB, 在所有 Java 提交中击败了55.36%的用户
*/
class Solution {
public boolean canJump(int[] nums) {
boolean[] dp = new boolean[nums.length];
dp[0]=true;
for(int i = 1 ;i<nums.length;i++){
for(int j = 0;j<i;j++){
if(dp[j]&&(j+nums[j])>=i){
dp[i] = true;
break;
}
}
}
return dp[nums.length-1];
}
}
5.不同路径 II(63):计数问题
和问题3差不多,只是多了一些对于初始条件的分析:首先当数组a[i,j]=1时,dp[i,j]=0,也就是说当前地方有障碍时,到达该地方的方法数为0
/*
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了40.16%的用户
*/
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
int m = obstacleGrid.length;
int n = obstacleGrid[0].length;
int [][]dp = new int[m][n];
//对原点进行判断,当原点有障碍或终点有障碍时,直接返回0
if(obstacleGrid[0][0]==1||obstacleGrid[m-1][n-1]==1){
return 0;
}else{
dp[0][0] = 1;
}
//对第一列进行赋值,当有一个点存在障碍时,后面的点都不能到达,不进行赋值,即为0
for(int i = 1;i<m;i++){
if(obstacleGrid[i][0]!=1){
dp[i][0] = 1;
}else{
break;
}
}
//同上
for(int j = 1;j<n;j++){
if(obstacleGrid[0][j]!=1){
dp[0][j] = 1;
}else{
break;
}
}
//f(i,j) = f(i-1,j)+f(i,j-1)
for(int i = 1;i<m;i++){
for(int j = 1; j< n ; j++){
if(obstacleGrid[i][j]==1) continue;
dp[i][j] = dp[i-1][j]+dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
6.粉刷房子(256):求最小值
/*
无会员,无运行结果。
*/
class Solution {
public int minCost(int[][] costs) {
if(costs==null||costs.length==0){
return 0;
}
int len = costs.length;
int [][]dp = new int[len+1][3];
dp[0][0] = 0;
dp[0][1] = 0;
dp[0][2] = 0;
for(int i = 1;i<=len;i++){
for(int j = 0;j<3;j++){
dp[i][0] = Math.min(dp[i-1][1]+costs[i-1][0],dp[i-1][2]+costs[i-1][1]);
dp[i][1] = Math.min(dp[i-1][0]+costs[i-1][1],dp[i-1][2]+costs[i-1][2]);
dp[i][2] = Math.min(dp[i-1][0]+costs[i-1][1],dp[i-1][0]+costs[i-1][2]);
}
}
return Math.min(dp[len][0],dp[len][1],dp[len][2]);
}
}
7.解码方法(91):求计数
一:确定状态:
)1:最终状态:长度为n的字符串的可解码次数,等于n-1,n-2的解码次数之和,但是要注意S[n-1]要能够组成一个英文字母,s[n-2]s[n-1]能够组成一个英文字母。
)2:子问题:要想求n解码的次数,转化成求n-1,n-2的解码次数之和
二:转移方程:dp[n] = dp[n-1](S[n-1]要能够组成一个英文字母)+p[n-2](s[n-2]s[n-1]能够组成一个英文字母)
三:初始条件和边界条件:dp[0]=1,当不需要解码时,解出来为空。
四:计算顺序:从下到上
/*
执行用时:1 ms, 在所有 Java 提交中击败了99.96%的用户
内存消耗:36.8 MB, 在所有 Java 提交中击败了67.84%的用户
*/
class Solution {
public int numDecodings(String s) {
if(s==null||s.length()==0){
return 1;
}
int[] dp = new int[s.length()+1];
//当不输入字符时,即只有一种解码方式,空白
dp[0]=1;
for(int i = 1;i<=s.length();i++){
dp[i] = 0;
int m = s.charAt(i-1)-'0';
//判断最后一位数字是否能够构成一个英文字母
if(m>=1&&m<=9){
dp[i] += dp[i-1];
}
if(i>=2){
//判断最后两个数字是否能构成英文字母
int n = (s.charAt(i-2)-'0')*10+s.charAt(i-1)-'0';
if(n<=26&&n>=10){
dp[i] += dp[i-2];
}
}
}
return dp[s.length()];
}
}
8.最长上升子序列(300):求计数
/*
执行用时:85 ms, 在所有 Java 提交中击败了5.47%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了5.02%的用户
*/
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
if(nums.length==1) return 1;
int []dp = new int[nums.length];
Arrays.fill(dp,1);
int maxLength = 0;
for(int i = 1;i<nums.length;i++){
for(int j = 0;j<i;j++){
if(nums[i]>nums[j]){
dp[i] = Math.max(dp[j]+1,dp[i]);
}
if(dp[i]>maxLength){
maxLength = dp[i];
}
}
}
return maxLength;
}
}
9.最小路径和(64):求最小值
/*
执行用时:3 ms, 在所有 Java 提交中击败了86.73%的用户
内存消耗:41.4 MB, 在所有 Java 提交中击败了44.82%的用户
*/
class Solution {
public int minPathSum(int[][] grid) {
if(grid==null||grid.length==0){
return 0;
}
int m = grid.length;
int n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
//初始化dp数组
for(int i = 1;i< m; i++){
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int j = 1;j<n;j++){
dp[0][j] = dp[0][j-1] + grid[0][j];
}
//从上方和左方选取较小值
for(int i = 1;i<m;i++){
for(int j= 1;j<n;j++){
dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[m-1][n-1];
}
}
10.Bomb enemy
/*
无会员,无法检验对错。
分成4个方向(上下左右)依次求值。
*/
class Solution {
public int[] maxKilledEnemies(char[][] grid) {
if(grid==null||grid.length==0||grid[0].length==0){
return 0;
}
int m = grid.length;
int n = grid[0].length;
int [][] res = new int[m][n];
int [][] temp = new int[m][n];
// 初始化 res
for(int i = 0;i< m;i++){
for(int j = 0;j<n;j++){
res[i][j] = 0 ;
}
}
//up
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
if(grid[i][j] == 'w'){
temp[i][j] = 0;
}else{
temp[i][j] = 0;
if(grid[i][j]=='E'){
temp[i][j] = 1;
}
if(i>0){
temp[i][j] = temp[i-1][j];
}
}
res[i][j]+=temp[i][j];
}
}
//down
for(int i = m-1;i>=0;i--){
for(int j = 0;j<n;j++){
if(grid[i][j] == 'w'){
temp[i][j] = 0;
}else{
temp[i][j] = 0;
if(grid[i][j]=='E'){
temp[i][j] = 1;
}
if(i+1<m){
temp[i][j] = temp[i+1][j];
}
}
res[i][j]+=temp[i][j];
}
}
//left
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
if(grid[i][j] == 'w'){
temp[i][j] = 0;
}else{
temp[i][j] = 0;
if(grid[i][j]=='E'){
temp[i][j] = 1;
}
if(j>0){
temp[i][j] = temp[i][j-1];
}
}
res[i][j]+=temp[i][j];
}
}
//right
for(int i = 0;i<m;i++){
for(int j = n-1;j>=0;j--){
if(grid[i][j] == 'w'){
temp[i][j] = 0;
}else{
temp[i][j] = 0;
if(grid[i][j]=='E'){
temp[i][j] = 1;
}
if(j+1<n){
temp[i][j] = temp[i][j+1];
}
}
res[i][j]+=temp[i][j];
}
}
//遍历寻找答案
int ans = 0
for(int i = 0;i< m;i++){
for(int j = 0;j<n;j++){
if(grid[i][j]=='0'){
if(res[i][j]>ans){
ans = res[i][j];
}
}
}
}
return ans;
}
}
11.比特位计数(338):求计数
/*
执行用时:1 ms, 在所有 Java 提交中击败了99.98%的用户
内存消耗:42.6 MB, 在所有 Java 提交中击败了53.72%的用户
*/
class Solution {
public int[] countBits(int num) {
int []dp = new int[num+1];
dp[0] = 0;
//dp[i/2]:判断i右移一位之后有几个1; i%2:判断i的最后一位是否是1
for(int i = 0;i<=num;i++){
dp[i] = dp[i/2] + i%2;
}
return dp;
}
}
12.paint house
class Solution {
public int minCost(int[][] costs) {
if(costs==null||costs.length==0){
return 0;
}
int len = costs.length;
int colors = costs[0].length;
int [][]dp = new int[len+1][colors];
int min, min2;
int minNumber=0, min2Number=0;
//初始条件
for(int j = 0; j<colors;j++ ){
dp[0][j] = 0;
}
for(int i = 1;i<=len;i++){
min = min2= Integer.MAX_VALUE;
//取出dp[i-1][k]的最小值和此小值,并记录位置
for(int j = 0;j<colors;j++){
if(dp[i-1][j]<min){
min2 = min;
min2Number = minNumber;
min = dp[i-1][j];
minNumber = j;
}else{
if(dp[i-1][j]<min2){
min2 = dp[i-1][j];
min2Number = j;
}
}
}
//状态转移方程:dp[i][j] = min{dp[i-1][z]}+cost[i-1][j](z!=j)
for(int j = 0;j<colors;j++){
if(j!=minNumber){
dp[i][j] = d[i-1][minNumber] + costs[i-1][j];
}else{
dp[i][j] = dp[i-1][min2Number] + costs[i-1][j];
}
}
}
// 循环判断,对于最后一栋房子涂什么颜色,会令总花费最小
int res = Integer.MAX_VALUE;
for(int i = 0;i<colors;i++){
if(dp[len][i]<res){
res = dp[len][i];
}
}
return res;
}
}
13.打家劫舍(198)
一:确定状态:
)1:最终状态:最后一间房子n偷还是不偷,偷的话n-1不能偷,就等于给n-2套房子选择+第n套房子中的财产;不偷就等于给n-1套房子进行选择
)2:子问题:i偷不偷,i偷,f(i-2)+nums[n-1];i不偷,f(i-1)
二:转移方程:f(i) = max(f(i-2)+num[i-1], f(i-1))
三:初始条件和边界条件:f(0) = 0, f(1) = nums[0], f(2) = max(nums[0],nums[1])
四:计算顺序:从下到上
/*
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.8 MB, 在所有 Java 提交中击败了77.75%的用户
*/
class Solution {
public int rob(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int len = nums.length;
int []dp = new int [len+1];
dp[0] = 0;
for(int i = 1;i<= len;i++){
if(i==1){
dp[i] = nums[0];
}else if(i==2){
dp[i] = Math.max(nums[0],nums[1]);
}else{
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
}
}
return dp[len];
}
}
14.打家劫舍Ⅱ(213)
分析:从最后一栋房子n开始,由于房子是环形摆放的,所以当最后一栋房子n在选择范围内时,第一栋房子和第n-1栋房子不能被选择,我们可以直接将第一栋房子舍去,[1,n]序号的房子就成了一排;同理当第一栋房子在选择范围内时,第n栋房子和第2栋房子不能被选择,[0,n-1]序号的房子就成了一排。
从而就变成了上面的那道题。
/*
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:36.1 MB, 在所有 Java 提交中击败了46.72%的用户
*/
class Solution {
public int rob(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
if(nums.length==1){
return nums[0];
}
int len = nums.length;
//当第一栋房子在选择目标内时
int ans = onlyOne(nums,0,len-1);
//当最后一栋房子在选择目标内时
int ans2 = onlyOne(nums,1,len);
//返回取值大的一种选择
return ans>ans2?ans:ans2;
}
public int onlyOne(int[] nums, int begin, int end){
//将选择的好的房子重新放置到一个新的数组
int[] copy = Arrays.copyOfRange(nums,begin,end);
int[] dp = new int[copy.length+1];
//初始化dp
dp[0] = 0;
for(int i = 1;i<=copy.length;i++){
if(i==1){
dp[i] = copy[0];
}else if(i==2){
dp[i] = Math.max(copy[0],copy[1]);
}else{
//状态转移方程
dp[i] = Math.max(dp[i-1],dp[i-2]+copy[i-1]);
}
}
return dp[copy.length];
}
}
15.买卖股票的最佳时机&&股票的最大利润(121):买卖一次
/*
执行用时:1 ms, 在所有 Java 提交中击败了98.55%的用户
内存消耗:38.4 MB, 在所有 Java 提交中击败了61.99%的用户
*/
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int minprice = Integer.MAX_VALUE;
int maxValue = Integer.MIN_VALUE;
for(int i = 0;i<len;i++){
//一直保存股票的最低价
if(prices[i]<minprice){
minprice = prices[i];
}
//不断更新股票的最大利润
int temp = prices[i]-minprice;
if(temp>maxValue){
maxValue = temp;
}
}
return maxValue;
}
}
16.买卖股票的最佳时机(122):买卖多次
/*
执行用时:1 ms, 在所有 Java 提交中击败了99.32%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了68.57%的用户
*/
//把所有上升阶段的利益全部吃到,即为利益最大
class Solution {
public int maxProfit(int[] prices) {
int res = 0;
for(int i = 0;i<prices.length-1;i++){
if(prices[i+1]>prices[i]){
res += prices[i+1]-prices[i];
}
}
return res;
}
}
/*
执行用时:4 ms, 在所有 Java 提交中击败了16.06%的用户
内存消耗:38.1 MB, 在所有 Java 提交中击败了92.30%的用户
*/
class Solution {
public int maxProfit(int[] prices) {
int [][]dp = new int[prices.length+1][2];
//初始化条件
dp[0][0] = 0;dp[0][1] = -prices[0];
for(int i = 1;i<=prices.length;i++){
//dp[i][0]:第i天并未拥有股票-->第i-1天也并没有拥有 or 第i-1天拥有,第i天卖了
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i-1]);
//dp[i][1]:第i天拥有股票-->第i-1天也拥有 or 第i-1天并未拥有,第i天买了
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i-1]);
}
//最后的结果,第n天未拥有股票
return dp[prices.length][0];
}
}
17.买卖股票的最佳时机(123):买卖两次,这时就需要记录买卖的次数
/*
执行用时:95 ms, 在所有 Java 提交中击败了38.89%的用户
内存消耗:58 MB, 在所有 Java 提交中击败了35.73%的用户
*/
class Solution {
public int maxProfit(int[] prices) {
// dp[i][2][3]:i-天、 2-股票拥有与否 、 3-买卖次数(买卖算一次),这里设置买的时候才会扣次数
int[][][] dp = new int[prices.length+1][2][3];
// 初始化
for(int i = 1;i<3;i++){
dp[0][0][i] = 0;
dp[0][1][i] = Integer.MIN_VALUE;
}
for(int i = 1;i<=prices.length;i++){
for(int j=2;j>=1;j--){
//状态转移方程
//第i天未拥有股票 = max{第i-1天未拥有股票,第i-1天拥有股票并且第i天卖了}
dp[i][0][j] = Math.max(dp[i-1][0][j],dp[i-1][1][j]+prices[i-1]);
//第i天拥有股票 = max{第i-1天拥有股票,第i-1天未拥有股票并且第i天买了}
dp[i][1][j] = Math.max(dp[i-1][1][j],dp[i-1][0][j-1]-prices[i-1]);
}
}
return dp[prices.length][0][2];
}
}
/*
执行用时:38 ms, 在所有 Java 提交中击败了43.50%的用户
内存消耗:51.5 MB, 在所有 Java 提交中击败了87.37%的用户
*/
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int [][]dp = new int[len+1][6];
//初始条件
dp[0][1] = 0;
dp[0][2]= dp[0][3] = dp[0][4] = dp[0][5] = Integer.MIN_VALUE;
for(int i = 1;i<=len;i++){
for(int j = 1;j<=5;j++){
//处于阶段1,3,5
if(j%2==1&&j>1&&i>=2){
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-1]+prices[i-1]-prices[i-2]);
}
//处于阶段2.4
if(j%2==0&&i-2>=0){
dp[i][j] = Math.max(dp[i-1][j]+prices[i-1]-prices[i-2],
Math.max(dp[i-1][j-1],dp[i-1][j-2]+prices[i-1]-prices[i-2]));
}
}
}
return dp[len][5];
}
}
18.买卖股票的最佳时机 IV(188):买卖k次
/*
执行用时:7 ms, 在所有 Java 提交中击败了60.11%的用户
内存消耗:37.6 MB, 在所有 Java 提交中击败了83.93%的用户
*/
class Solution {
public int maxProfit(int k, int[] prices) {
if(k>prices.length/2){
int ans = 0;
for(int i= 1;i<prices.length;i++){
if(prices[i]>prices[i-1]){
ans+=prices[i]-prices[i-1];
}
}
return ans;
}
int [][][]dp = new int[prices.length+1][2][k+1];
for(int i =1;i<k+1;i++){
dp[0][0][i] = 0;
dp[0][1][i] = Integer.MIN_VALUE;
}
for(int i=1;i<prices.length+1;i++){
for(int j=k;j>=1;j--){
dp[i][0][j]=Math.max(dp[i-1][0][j],dp[i-1][1][j]+prices[i-1]);
dp[i][1][j]=Math.max(dp[i-1][1][j],dp[i-1][0][j-1]-prices[i-1]);
}
}
return dp[prices.length][0][k];
}
}
/*
执行用时:
3 ms, 在所有 Java 提交中击败了96.33%的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了82.50%的用户
*/
class Solution {
public int maxProfit(int k, int[] prices) {
if(prices==null||prices.length==0){
return 0;
}
int len = prices.length;
int [][]dp = new int[len+1][2*k+1+1];
//初始条件
dp[0][1] = 0;
for(int i = 2;i<=2*k+1;i++){
dp[0][i] = Integer.MIN_VALUE;
}
for(int i = 1;i<=len;i++){
for(int j = 1;j<=2*k+1;j++){
//处于阶段1,3,5
if(j%2==1&&j>1&&i>=2){
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-1]+prices[i-1]-prices[i-2]);
}
//处于阶段2.4
if(j%2==0&&i-2>=0){
dp[i][j] = Math.max(dp[i-1][j]+prices[i-1]-prices[i-2],
Math.max(dp[i-1][j-1],dp[i-1][j-2]+prices[i-1]-prices[i-2]));
}
}
}
return dp[len][2*k+1];
}
}
19.最佳买卖股票时机含冷冻期(309)
/*
执行用时:1 ms, 在所有 Java 提交中击败了99.38%的用户
内存消耗:37.7 MB, 在所有 Java 提交中击败了47.60%的用户
*/
class Solution {
public int maxProfit(int[] prices) {
int[][] ans = new int[prices.length+1][2];
ans[0][1] = Integer.MIN_VALUE;
ans[0][0] = 0;
for (int i =1;i<prices.length+1;i++){
//卖出之后,有一天冷静期才能买入
//冷静期对第一天无效
if (i-2<0){
ans[i][0] = Math.max(ans[i-1][0],ans[i-1][1]+prices[i-1]);
ans[i][1] = Math.max(ans[i-1][1],-prices[i-1]);
}else{
ans[i][0] = Math.max(ans[i-1][0],ans[i-1][1]+prices[i-1]);
ans[i][1] = Math.max(ans[i-1][1],ans[i-2][0]-prices[i-1]);
}
}
return ans[prices.length][0];
}
}
20.买卖股票的最佳时机含手续费(714)
/*
执行用时:5 ms, 在所有 Java 提交中击败了64.88%的用户
内存消耗:47.7 MB, 在所有 Java 提交中击败了61.75%的用户
*/
class Solution {
public int maxProfit(int[] prices, int fee) {
if(prices==null||prices.length==0){
return 0;
}
int dp_i_1 = Integer.MIN_VALUE;
int dp_i_0 = 0;
for(int i = 1;i<prices.length+1;i++){
dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i-1]);
dp_i_1 = Math.max(dp_i_1,dp_i_0-prices[i-1]-fee);
}
return dp_i_0;
}
}
21.俄罗斯套娃信封问题(354)
/*
执行用时:301 ms, 在所有 Java 提交中击败了21.89%的用户
内存消耗:39.6 MB, 在所有 Java 提交中击败了48.79%的用户
*/
class Solution {
public int maxEnvelopes(int[][] envelopes) {
if(envelopes==null||envelopes.length==0){
return 0;
}
//先对信封进行排序
Arrays.sort(envelopes,(a,b)->(a[0]!=b[0]?a[0]-b[0]:a[1]-b[1]));
int n = envelopes.length;
int[] dp = new int[n];
int res = 0;
for(int i = 0;i<n;i++){
// 初始化dp数组,每个信封都可以装他自己,即为1
dp[i] = 1;
for(int j = 0;j<i;j++){
//一定要注意条件,长宽都比另一个信封大的时候才能装进去
if(envelopes[i][0]>envelopes[j][0]&&envelopes[i][1]>envelopes[j][1]){
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
res = Math.max(res,dp[i]);
}
return res;
}
}
22.完全平方数(279)
/*
执行用时:41 ms, 在所有 Java 提交中击败了75.85%的用户
内存消耗:37.7 MB, 在所有 Java 提交中击败了69.40%的用户
*/
class Solution {
public int numSquares(int n) {
int[] dp = new int[n+1];
//这里不要设置Integer.MAX_VALUE,可设置的范围[n,Integer.MAX_VALUE)
Arrays.fill(dp,n);
//初始条件
dp[0]=0;
for(int i = 1;i<=n;i++){
for(int j = 1;j*j<=i;j++){
if(i-j*j>=0){
dp[i] = Math.min(dp[i],dp[i-j*j]+1);
}
}
}
return dp[n];
}
}
23.分割回文串 II(132)
/*
执行用时:1165 ms, 在所有 Java 提交中击败了22.03%的用户
内存消耗:38.5 MB, 在所有 Java 提交中击败了50.40%
的用户
*/
class Solution {
public int minCut(String s) {
if(s==null||s.length()==0){
return 0;
}
int[] dp = new int[s.length()+1];
dp[0] = 0;
for(int i = 1;i< s.length()+1;i++){
dp[i] = Integer.MAX_VALUE;
for(int j =0;j<i;j++){
if(judge(s.substring(j,i))){
dp[i] = Math.min(dp[i],dp[j]+1);
}
}
}
//题目问的是最少划分几次,而求得的是dp[i]:i最少可以划分成几个回文串
return dp[s.length()]-1;
}
//判断所传入的字符串是否为回文数
public boolean judge(String temp){
int len = temp.length();
if(len==1){
return true;
}
for(int i = 0;i<len/2;i++){
if(temp.charAt(i)!=temp.charAt(len-i-1)){
return false;
}
}
return true;
}
}
24.copy books
25.Coins in a Line:博弈型动态规划
题目:
)1.有一排N个石子,A、B两人轮流取石子
)2.每次一个人从最右边取走1个或者2个石子
)3.取走最后石子的人胜利
)4.问A先手是否必胜
例:N=5 true
public boolean firstWillWin(int n){
if(n==0) return false;
boolean[] dp = new int[n+1];
// 当只有1个和2个石子的时候,必胜。
dp[1] = true;
dp[2] = true;
//dp[i]:面对i个石子,是否先手会胜
for(int i = 3;i<=n;i++){
//i为A走,则i-1 or i-2是对手B走。
dp[i] = !dp[i-1]||!dp[i-2];
}
return dp[n];
}
26.背包DP{
1.最大承重问题 and 恰好取到背包容量--可行性问题
2.承重组合数(每个Ai只能用一次)和(每个Ai可用多次)--计数问题
3.最大价值(每个Ai只能用一次)和(每个Ai可用多次)--最值问题
}
)1.最大承重问题 and 恰好取到背包容量
最大承重问题:给定N个物品,重量分别是A0,A1,...,An-1;一个背包的最大容量为M;问最多能带走多重的物品
例:in:[2,3,5,7];M=11
out:10
public int backPack(int M, int[] A){
if(A==null||A.length==0){
return null;
}
int len = A.length;
int[][] dp = new int[len+1][W+1];
//初始化数组
dp[0][0]=true;
for(int i = 1;i<=len;i++){
dp[i][0] = false;
}
for(int i=1;i<=len;i++){
for(int j =0;j<=W;j++){
dp[i][j] = dp[i-1][j];
if(j-A[i-1]>=0){
dp[i][j] = dp[i-1][j]||dp[i-1][j-A[i-1]];
}
}
}
//依次往下遍历,找到一个可以填满的即为最大!
for(int i = W;i>=0;i--){
if(dp[len][i]){
return i;
}
}
}
恰好取到背包容量:分割等和子集(416)
/*
执行用时:50 ms, 在所有 Java 提交中击败了31.25%的用户
内存消耗:39 MB, 在所有 Java 提交中击败了52.75%的用户
*/
class Solution {
public boolean canPartition(int[] nums) {
if(nums==null||nums.length==0){
return false;
}
int sum = 0;
for(int i:nums){
sum+=i;
}
//当数据集的总和为奇数时,不可能等分
if(sum%2==1) return false;
//判断前n个是否能够分出sum/2的元素和
boolean[][] dp = new boolean[nums.length+1][sum/2+1];
//初始条件
dp[0][0] = true;
for(int i = 1;i<nums.length+1;i++){
for(int j = 0;j<sum/2+1;j++){
//dp[i-1][j]:前i-1个是否能够得到j元素和
//dp[i-1][j-nums[i-1]]:前i-1是否能够得到j-nums[i-1]元素和
if(j-nums[i-1]>=0){
dp[i][j] = dp[i-1][j]||dp[i-1][j-nums[i-1]];
}else{
dp[i][j] = dp[i-1][j];
}
}
}
return dp[nums.length][sum/2];
}
}
)2.承重组合数(每个Ai只能用一次)和(每个Ai可用多次)
(每个Ai只能用一次)
题意:给定N个正整数,A0,A1,...,An-1;一个正整数Target;求有多少种组合加起来是target;每个Ai只能用一次
in:A=[1,2,3,3,7] Target=7
out:2(7=7,1+3+3=7)
class Solution {
public int backPack(int[] nums,int Target) {
if(nums==null||nums.length==0){
return null;
}
int len = nums.length;
int[][] dp = new int[len+1][Target+1];
dp[0][0] = 1;
for(int i=1;i<=Target;i++){
dp[0][i]=0;
}
for(int i = 1;i<=len;i++){
for(int j = 1;j<=Target;j++){
if(j-nums[i-1]>=0){
dp[i][j] = dp[i][j]+dp[i-1][j-nums[i-1]];
}
}
}
return dp[len][Target];
}
(每个Ai可用多次)
题意:给定N个正整数,A0,A1,...,An-1;一个正整数Target;求有多少种组合加起来是target;每个Ai可用多次
class Solution {
public int backPack(int[] nums,int Target) {
if(Target==0){
return 1;
}
if(nums==null||nums.length==0){
return null;
}
int len = nums.length;
int[] dp = new int[Target+1];
dp[0] = 1;
for(int i = 1;i<=len;i++){
for(int j = 1;j<=Target;j++){
if(j-nums[i-1]>=0){
dp[j] = dp[j]+dp[j-nums[i-1]];
}
}
}
return dp[Target];
}
)3.最大价值(每个Ai只能用一次)和(每个Ai可用多次)
(每个Ai只能用一次)
题意:给定N个正整数,A0,A1,...,An-1;价值分别为正整数V0,V1,...,Vn-1;最大承重量M;求最多能带走多大价值的物品
in:weigth[2,3,5,7];value[1,5,2,4];W=11
out:9
(二维数组版)
class Solution {
public int backPack(int m, int[] nums,int[] values) {
if(nums==null||nums.length==0){
return null;
}
int len = nums.length;
int[][] dp = new int[len+1][m+1];
//初始化条件
dp[0][0] = 0;
for(int i = 1;i<=m;i++){
dp[0][i] = -1;
}
for(int i = 1;i<=len;i++){
for(int j = 0;j<=m;j++){
dp[i][j] = dp[i-1][j];
if(j-nums[i-1]>=0&&dp[i-1][j-nums[i-1]]!=-1){
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-nums[i-1]]+values[i-1]);
}
if(dp[i][j]>max){
max = dp[i][j];
}
}
}
return max;
}
(一维数组版)
class Solution {
public int backPack(int m, int[] nums,int[] values) {
if(nums==null||nums.length==0){
return null;
}
int len = nums.length;
int[] dp = new int[m+1];
int max = 0;
//初始化条件
dp[0] = 0;
for(int i = 1;i<=m;i++){
dp[i] = -1;
}
for(int i = 1;i<=len;i++){
for(int j = m;j>=0;j--){
if(j-A[i-1]>=0&&dp[j-A[i-1]]!=-1){
f[j] = Math.max(f[j],f[j-A[i-1]]+values[i-1]);
}
if(f[j]>max){
max = f[j];
}
}
}
return max;
}
(每个Ai可用多次)
题意:给定N个正整数,A0,A1,...,An-1;价值分别为正整数V0,V1,...,Vn-1;最大承重量M;每种物品都有无穷多个。求最多能带走多大价值的物品
in:weigth[2,3,5,7];value[1,5,2,4];W=10
out:15 (3个物品一,w=3*3=9,v=3*5=15)
(一维数组版)
class Solution {
public int backPack(int m, int[] nums,int[] values) {
if(nums==null||nums.length==0){
return null;
}
int len = nums.length;
int[] dp = new int[m+1];
int max = 0;
//初始化条件
dp[0] = 0;
for(int i = 1;i<=m;i++){
dp[i] = -1;
}
for(int i = 1;i<=len;i++){
for(int j = 0;j<=m;j++){
if(j-A[i-1]>=0&&dp[j-A[i-1]]!=-1){
f[j] = Math.max(f[j],f[j-A[i-1]]+values[i-1]);
}
if(f[j]>max){
max = f[j];
}
}
}
return max;
}
27.最长回文子序列(516)
/*
执行用时:58 ms, 在所有 Java 提交中击败了10.84%的用户
内存消耗:48.7 MB, 在所有 Java 提交中击败了38.44%的用户
*/
class Solution {
public int longestPalindromeSubseq(String s) {
int len = s.length();
if (len==0||s==null) return 0;
int[][] dp = new int[len][len];
//初始化数组,当只有一个字符时,回文序列的长度为1
for (int i = 0;i<len;i++) dp[i][i] = 1;
//sub:序列[i-j]中字符的个数
for(int sub=2;sub<=len;sub++){
for(int i = 0;i<=len-sub;i++){
int j = i+sub-1;
//先找出[i+1,j]和[i,j-1]中的最长回文子序列
dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);
//当s[i]==s[j]时
if(s.charAt(i)==s.charAt(j)){
dp[i][j] = Math.max(dp[i+1][j-1]+2,dp[i][j]);
}
}
}
return dp[0][len-1];
}
}
28.Coins in a Line(877. 石子游戏)
题目:
)1.给定一个序列a[n]
)2.A、B两人轮流取石子
)3.每次一个人每次只能取第一个数或者最后一个数
)4.双方都用最优策略,使得自己的数字和尽量比对手大
)5.问先手是否必胜(如何数字和一样,也算先手胜)
in:[1,5,233,7]
out:True(先手取走1,无论后手取哪个,先手都能取走233)
//面向测试用例编程
class Solution {
public boolean stoneGame(int[] piles) {
return true;
}
}
/*
执行用时:10 ms, 在所有 Java 提交中击败了23.12%的用户
内存消耗:38.9 MB, 在所有 Java 提交中击败了47.25%的用户
*/
class Solution {
public boolean stoneGame(int[] piles) {
if(piles==null||piles.length==0){
return true;
}
int len = piles.length;
int[][] dp = new int[len][len];
//当该序列中只有一个数字时,两人数字和之差就为该数字
for(int i = 0;i<len;i++){
dp[i][i] = piles[i];
}
//i:划分序列初始位置值 j:划分序列的终止位置值
for(int sub = 2;sub<=len;sub++){
for(int i = 0;i<=len-sub;i++){
int j = i+sub-1;
//当取第一件物品时 vs 取最后一件物品
dp[i][j] = Math.max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
}
}
return dp[0][len-1]>=0?true:false;
}
}
29.扰乱字符串(87)
30.戳气球(312)
/*
执行用时:92 ms, 在所有 Java 提交中击败了12.64%的用户
内存消耗:37.8 MB, 在所有 Java 提交中击败了21.98%的用户
*/
class Solution {
public int maxCoins(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int len = nums.length;
//定义数组temp,将其0和n+1的位置设置为1,其他位置 temp[i]=nums[i-1]
int[] temp = new int[len+2];
temp[0] = 1;
temp[len+1] = 1;
for(int i = 1;i<=len;i++){
temp[i] = nums[i-1];
}
//定义dp数组,dp[i][j]:扎破i+1一j-1号气球的最多获得的金币数
int[][] dp = new int[len+2][len+2];
//初始化dp数组
for(int i = 0;i<len+1;i++){
dp[i][i+1] = 0;
}
for(int sub = 3;sub<=len+2;sub++){
//i:区间的左端点 j:区间的右端点
for(int i=0;i<=len+2-sub;i++){
int j = i+sub-1;
dp[i][j] = 0;
//k:选择i+1一j-1中的一个气球作为最后被扎破的对象
//这时区间[i,j]被分成了[i,k] 和 [k,j]
//最后k的乘积值:temp[i]*temp[k]*temp[j]
for(int k = i+1;k<j;k++){
dp[i][j] = Math.max(dp[i][j],dp[i][k]+dp[k][j]+temp[i]*temp[k]*temp[j]);
}
}
}
return dp[0][len+1];
}
}
31.最长公共子序列(1143)
/*
执行用时:17 ms, 在所有 Java 提交中击败了13.68%的用户
内存消耗:41.9 MB, 在所有 Java 提交中击败了85.18%的用户
*/
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int [][] dp = new int[text1.length()+1][text2.length()+1];
//初始化数组,空串和任何的序列的公共子序列为0
for (int i =0;i<text1.length();i++) dp[i][0] = 0;
for (int i = 0;i<text2.length();i++) dp[0][i] = 0;
for (int i = 1;i<=text1.length();i++){
for (int j = 1;j<=text2.length();j++){
//当text1和text2中的最后一个字符相等时,则接下来进行判断其它字符
if (text1.charAt(i-1)==text2.charAt(j-1)) dp[i][j] = 1+dp[i-1][j-1];
//当最后一个字符不相等时,选择舍去text1、text2两个其中一个的最后一个值
else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
}
}
return dp[text1.length()][text2.length()];
}
}
32.交错字符串(97)
/*
执行用时:4 ms, 在所有 Java 提交中击败了63.85%的用户
内存消耗:36.3 MB, 在所有 Java 提交中击败了97.42%的用户
*/
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
int len1 = s1.length();
int len2 = s2.length();
int len3 = s3.length();
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
char[] c3 = s3.toCharArray();
//当len(s1)+len(s2)!=len(s3)时,肯定不符合条件,返回false
if(len1+len2!=len3){
return false;
}
boolean[][] dp = new boolean[len1+1][len2+1];
//初始化数组
dp[0][0] = true;
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2;j++){
//当c1的最后一个字符和c3最后一个字符相同时,c1长度减一,继续判断[i-1][j],注意这时并不需要j>0.
if (i>0&&c1[i-1]==c3[i+j-1]){
dp[i][j] = dp[i][j]||dp[i-1][j];
}
i
//当c2的最后一个字符和c3最后一个字符相同时,c2长度减一,继续判断[i][j-1],注意这时并不需要i>0.
if(j>0&&c2[j-1]==c3[i+j-1]){
dp[i][j] = dp[i][j]||dp[i][j-1];
}
}
}
return dp[len1][len2];
}
}
33.编辑距离(72)
/*
执行用时:5 ms, 在所有 Java 提交中击败了95.53%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了85.35%的用户
*/
class Solution {
public int minDistance(String word1, String word2) {
int len1 = word1.length();
int len2 = word2.length();
char[] c1 = word1.toCharArray();
char[] c2 = word2.toCharArray();
int[][] dp = new int[len1+1][len2+1];
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2; j++){
//当word1和word2中有空串的情况出现
if(i==0||j==0){
// 当word1为空时,只需增加操作
if(i==0){
dp[i][j] = j;
}else{
// 当word2为空时,只需删除操作
dp[i][j] = i;
}
}else{
//dp[i][j-1]+1:word1最后增加word2[n],word2左移一位,操作数加1
//dp[i-1][j]+1: word1删除最后一位,word1左移一位,操作数加1
//dp[i-1][j-1]+1:word1把最后一位替换成word2[n],两个字符串分别左移一位,操作数加1
dp[i][j] = Math.min(dp[i][j-1]+1, Math.min(dp[i-1][j]+1,dp[i-1][j-1]+1));
//dp[i-1][j-1]:最后一位相等,两个字符串分别左移一位,操作数无变化。
if(c1[i-1]==c2[j-1]){
dp[i][j] = Math.min(dp[i-1][j-1],dp[i][j]);
}
}
}
}
return dp[len1][len2];
}
}
34.不同的子序列(115)
/*
执行用时:3 ms, 在所有 Java 提交中击败了94.89%的用户
内存消耗:36.8 MB, 在所有 Java 提交中击败了64.26%的用户
*/
class Solution {
public int numDistinct(String s, String t) {
int len1 = s.length();
int len2 = t.length();
if(len1<len2){
return 0;
}
char[] c1 = s.toCharArray();
char[] c2 = t.toCharArray();
int[][] dp = new int[len1+1][len2+1];
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2;j++){
if(i==0||j==0){
if(j==0){
dp[i][j] = 1;
}else{
dp[i][j] = 0;
}
}else{
dp[i][j] = dp[i-1][j];
if(c1[i-1]==c2[j-1]){
dp[i][j] = dp[i][j]+dp[i-1][j-1];
}
}
}
}
return dp[len1][len2];
}
}
35.正则表达式匹配(10)
/*
执行用时:2 ms, 在所有 Java 提交中击败了99.30%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了48.33%的用户
*/
class Solution {
public boolean isMatch(String s, String p) {
int len1 = s.length();
int len2 = p.length();
char[] c1 = s.toCharArray();
char[] c2 = p.toCharArray();
boolean[][] dp = new boolean[len1+1][len2+1];
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2;j++){
//初始化数组
if(j==0){
if(i==0){
//当s和p都为0时,dp[i][j]=true;
dp[i][j] = true;
}else{
//只有当p为0时,dp[i][j]=false;
dp[i][j] = false;
}
}else{
dp[i][j] = false;
//当p[j]为‘.’或者p[j]==s[i],两个字符串均左移
if(i>0&&(c2[j-1]=='.'||c2[j-1]==c1[i-1])){
dp[i][j] = dp[i-1][j-1];
//当p[j]为‘*’时,分为两种情况
// 1.p[j-1]p[j]所组成的字符串并不是所需的字符串,因此p[j-1]*表示0个p[i-1],p左移两位
// 2.为所需字符串,则s左移一位
}else if(c2[j-1]=='*'){
dp[i][j] = dp[i][j-2];
if(i>0&&j-2>=0&&(c2[j-2]=='.'||c2[j-2]==c1[i-1])){
dp[i][j]|=dp[i-1][j];
}
}
}
}
}
return dp[len1][len2];
}
}
36.通配符匹配(44)
/*
执行用时:22 ms, 在所有 Java 提交中击败了80.21%的用户
内存消耗:39 MB, 在所有 Java 提交中击败了52.59%的用户
*/
class Solution {
public boolean isMatch(String s, String p) {
int len1 = s.length();
int len2 = p.length();
char[] c1 = s.toCharArray();
char[] c2 = p.toCharArray();
boolean[][] dp = new boolean[len1+1][len2+1];
for(int i = 0;i<=len1;i++){
for(int j = 0;j<=len2;j++){
if(j==0){
if(i==0){
dp[i][j] = true;
}else{
dp[i][j] = false;
}
}else{
dp[i][j] = false;
//当p[j]=s[i] or p[j]='?'时,p和s左移一位
if(i>0&&(c1[i-1]==c2[j-1]||c2[j-1]=='?')){
dp[i][j] = dp[i-1][j-1];
//当p[j]为‘*’时,分为两种情况
// 1.p[j]并不是所需的字符串,所以p左移一位
// 2.为所需字符串,则s左移一位
}else if(c2[j-1]=='*'){
dp[i][j] = dp[i][j-1];
if(i>0){
dp[i][j] |= dp[i-1][j];
}
}
}
}
}
return dp[len1][len2];
}
}
37.一和零(474)
/*
执行用时:359 ms, 在所有 Java 提交中击败了5.01%的用户
内存消耗:67.3 MB, 在所有 Java 提交中击败了19.32%的用户
*/
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//类似于背包问题,从最后一个字符串考虑,判断其在不在子集中
//限制条件,字符串的数目,数字0的个数m,数字1的个数n
//建立3维数组
int[][][] dp = new int[strs.length+1][m+1][n+1];
for(int i = 0;i<=strs.length;i++){
for(int j = 0;j<=m;j++){
for(int k = 0;k<=n;k++){
//当数组中元素的个数为0时,其最大子集为0
if(i==0){
dp[i][j][k] = 0;
}else{
//初始化dp[i][j][k]:不把strs[i]装入子集
dp[i][j][k] = dp[i-1][j][k];
//获取strs[i]中0和1的个数
int len0 = 0, len1 = 0;
for(int z = 0;z<strs[i-1].length();z++){
if(strs[i-1].charAt(z)=='0'){
len0++;
}else if(strs[i-1].charAt(z)=='1'){
len1++;
}
}
//如果strs[i]中0和1的个数小于规定值,判断装还是不装strs[i]
// !!!一定记得装strs[i]时,方法数要加1
if(j-len0>=0&&k-len1>=0){
dp[i][j][k] = Math.max(dp[i][j][k],dp[i-1][j-len0][k-len1]+1);
}
}
}
}
}
return dp[strs.length][m][n];
}
}