动态规划刷题(3)

一)按摩师:

这个题和打家劫舍(1)一模一样

面试题 17.16. 按摩师 - 力扣(LeetCode)

我可以跳过1个,也可以跳过两个,还可以跳过三个

1)定义一个状态标识:再进行分析题目的时候,这是一个总左向右推导的模型,根据经验+题目要求

dp[i]表示选择到i位置的时候,此时的最长预约时间,是可以继续划分出两个子状态

2)根据状态表示推导状态转移方程:从最近的一步划分问题

1)f(i)表示如果选取了这个i位置的值,nums[i]必选,此时的最长预约时长

此时选择了i位置的值,那么此时一定是不会选择i-1位置的值,此时f(i)=g(i-1)+array[i]

2)g(i)表示不选择i这个位置的值,nums[i]必不会选,此时选择的最长预约市场就是

2.1)如果i位置不进行选择,如果选择i-1位置的值,那么g(i)=f(i-1)

2.2)如果i位置不进行选择,如果不选择i-1位置的值,那么g(i)=g(i-1)

3)初始化:保证在进行填写dp表的时候数组不会发生越界

g(0)表示不选择0这个位置的时候,最长的预约时长就是g(0)=0

f(0)表示选择0这个位置的时候,最长的预约时长就是f(0)=array[0]

4)填表顺序:保证在进行计算dp[i]的时候,保证他所需要的值已经计算过了,从左向右的时候,两个表同时进行填写

5)返回值:

这个返回值又分为两种情况,要么最后一个位置选,要么最后一个位置不选

return Math.MAX(f(n-1),g(n-1)

初始化操作:保证在进行计算dp[i]的时候,数组不越界

class Solution {
    public int massage(int[] array) {
      if(array.length==0||array==null) return 0;
//1.创建两个dp表,这个题只要两天不被同时选中即可
int[] f=new int[array.length];//这个dp表表示,如果选择当前位置的值,那么最终计算出来的总预约时长
int[] g=new int[array.length];//这个dp表表示,如果如果不选择当前位置的值,那么最终计算出来的总的预约时长
//2.初始化
f[0]=array[0];
g[0]=0;
//3.填表
  for(int i=1;i<array.length;i++){
//3.1.如果选择i当前位置的值,前面的那个值i-1一定不会被选中,那么到达i位置的时候所需要的总预约时长是
     f[i]=g[i-1]+array[i];
//3.2.如果不选择i位置的值,那么i前面的那一个值是有可能被选中也有可能没有被选中
//如果前面的值被选中g(i)=f(i-1),如果前面的值没有被选中,那么g(i)=g(i-1)
     g[i]=Math.max(f[i-1],g[i-1]);
  }
//4.返回值
   return Math.max(f[array.length-1],g[array.length-1]);
  }
}

二)打家劫舍(2)

213. 打家劫舍 II - 力扣(Leetcode)

1)思路1:

假设考虑偷0号位置,那么在0号位置到array.length-1位置做一次打家劫舍的操作即可

假设不考虑偷0号位置,那么从1号位置到array.length-2这些位置做一次打家劫舍的操作即可

lass Solution {
    public int rob(int[] array) {
        //1.处理特殊情况
        if(array==null){
            return 0;
        }else if(array.length==1){
            return array[0];
        }else if(array.length==2){
            return Math.max(array[0],array[1]);
        }
        int[] f=new int[array.length];
        int[] ff=new int[array.length];
        int[] gg=new int[array.length];
        int[] g=new int[array.length];
        f[0]=array[0];
        g[0]=0;
        //选择从0号位置开始偷,肯定就不能选择最后一个位置偷
        for(int i=1;i<array.length-1;i++){
            f[i]=g[i-1]+array[i];//偷当前的财产
            g[i]=Math.max(f[i-1],g[i-1]);//不偷当前的财产,说明当前的i位置的值是不可以加的
        }
       int temp1=Math.max(f[array.length-1-1],g[array.length-1-1]);
       ff[1]=array[1];
       gg[1]=0;
       //要是选择偷最后一个位置,肯定就不可以选择偷第一个位置
        for(int i=2;i<array.length;i++){
            ff[i]=gg[i-1]+array[i];//偷当前的财产
            gg[i]=Math.max(ff[i-1],gg[i-1]);//不偷当前的财产,说明当前的i位置的值是不可以加的
        }
       int temp2=Math.max(ff[array.length-1],gg[array.length-1]);

       return Math.max(temp1,temp2);
    }
}
最优解法:2)思路2:可以通过分类讨论,将环形的问题,转化成两个线性打家劫舍1问题

1)假设一定偷0号位置的钱,那么第1个位置肯定不可以偷,最后一个位置肯定也是不可以偷的,那么只需要从(2,n-2)的位置这一段区间内偷的最大钱数即可

2)假设不偷0号位置的钱,那么从1号位置到n-1号位置做一次打家劫舍的操作即可

3)然后我们只需要1情况到2情况的最大值即可

1)确定一个状态表示:根据经验和题目要求,dp[i]表示从起始位置开始到当前位置所偷的钱的最大数目

f(i)表示偷到i位置的时候,偷array[i],此时的最大金额

g(i)表示偷到i位置的时候,不偷array[i],此时的最大金额

2)确定状态转移方程:

偷当前i位置的钱:f[i]=g[i-1]+array[i]

不偷当前位置的钱:g[i]=Math.max(f[i-1],g(i-1));

3)初始化:g(0)=0,f(0)=array[0]

4)填表顺序:从左到右,两个表一起进行填写

class Solution {
    public int GetRob(int left,int right,int[] array){
//1.创建dp表,创建数组原始规模的大小,多余的格子可以不填
        int[] f=new int[array.length];
        int[] g=new int[array.length];
//2.初始化
        f[left]=array[left];
        g[left]=0;
        for(int i=left+1;i<=right;i++){
            f[i]=g[i-1]+array[i];
            g[i]=Math.max(f[i-1],g[i-1]);
        }
      return Math.max(g[right],f[right]);
    }
    public int rob(int[] array) {
        int n=array.length;
        if(n==0) return 0;
        else if(n==1) return array[1];
        else if(nums.length==2) return Math.max(nums[0],nums[1]);
        return Math.max((GetRob(2,n-2,array)+array[0]),GetRob(1,n-1,array));
    }
}
class Solution {
    public int GetRob(int left,int right,int[] array){
            if(left>right) return 0;
            //第一种方式是开辟一个和原数组一样的数组,就不用考虑下标的映射关系
            //第二种方法就是开辟一个区间范围内的数组,但是需要考虑下标之间的映射关系
            int[] f=new int[right-left+1];
            int[] g=new int[right-left+1];
            f[0]=array[left];
            g[0]=0;
            for(int i=left+1;i<=right;i++){
                f[i-left]
                        =g[i-1-left]+array[i];
                g[i-left]=Math.max(f[i-1-left],g[i-1-left]);
            }
            return Math.max(g[right-left],f[right-left]);
    }
    public int rob(int[] array) {
         int n=array.length;
            if(n==0) return 0;
            else if(n==1) return array[0];
            return Math.max((GetRob(2,n-2,array)+array[0]),GetRob(1,n-1,array));
    }
}

三)删除并获得点数:

740. 删除并获得点数 - 力扣(Leetcode)

3.1)题目要求:

1 1 2 2 2 4 5 5 6 6 7 7 7

1)假设我先删5,那么目标值等于5的值会被全部选中,此时删除的就是4和6的值

此时获得的点数就是:5+5=10

2)然后选择2,这个时候选择的是两个2,那么点数是1的元素会被全部删除掉

此时获得的点数就是:2+2+2=8

3)此时选择7,那么目标值是6的元素(已经被删除)和目标值是8的元素会被删除掉(数组中并不存在此元素),此时所获取的点数就是7+7+7=21

所以最终此序列获得的最大点数是:10+8+21

4)假设此时数组是有序的,况且每一个元素只是出现了一次,那么此时这个时候就和打家劫舍问题非常类似了:假设数组的元素是1 2 3 4 5

1 2 3 4 5

此时选择1就不可以选择2了,只能接下来选择3,选择3就不可以选择4了,直接可以选择5,就是不可以选择相邻的元素,因为相邻的元素全部被删除掉了

5)但是实际上这些数可能不是连续的,还有假设你选择删除3,那么3前面的数和3后面的数需要全部进行删除,不能计算到最终的结果里面

3.2)解题思路:

1)确定一个状态表示:这是一个线性模型,一般做题思路就是经验+题目要求

f(i)表示如果选择i下标这个位置的数,所获得的点数的最大值,选择到i位置的时候,nums[i]必选,此时可以获得的最大点数

g(i)表示如果不选择i下标的数,所获得的点数的最大值,表示选择到i位置的时候,nums[i]不进行选择,此时可以获得的最大点数

以某一个位置为结尾,巴拉巴拉
到达i位置的时候,一共有两种情况,要么你选择i位置,要么你不选择i位置
从左向右填表,两个表一起填
class Solution {
    public int deleteAndEarn(int[] nums) {
        if(nums.length==1) return nums[0];
//1.首先进行预处理操作
//其实也可以遍历原来数组的最大值和最小值,新创建的数组的大小就是MAX-MIN+1即可,也可以看题目给定的数据范围
   long[] array=new long[200000];
   for(int i=0;i<nums.length;i++){
       array[nums[i]]=nums[i]+array[nums[i]];
    }
    // System.out.println(Arrays.toString(array));
    // return 10;
//2.定义两个数组
    long[] f=new long[array.length];
    long[] g=new long[array.length];
    f[0]=array[0];
    g[0]=0;
    for(int i=1;i<array.length;i++){
       f[i]=g[i-1]+array[i];
       g[i]=Math.max(f[i-1],g[i-1]);
    }
//3.返回数组中的最大值
   return (int)Math.max(f[array.length-1],g[array.length-1]);

  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值