力扣 1402. 做菜顺序

2023-11-23

难度 困难

原题

1402. 做菜顺序 - 力扣(LeetCode)

一个厨师收集了他 n 道菜的满意程度 satisfaction ,这个厨师做出每道菜的时间都是 1 单位时间。

一道菜的 「 like-time 系数 」定义为烹饪这道菜结束的时间(包含之前每道菜所花费的时间)乘以这道菜的满意程度,也就是 time[i]*satisfaction[i] 。

返回厨师在准备了一定数量的菜肴后可以获得的最大 like-time 系数 总和。

你可以按 任意 顺序安排做菜的顺序,你也可以选择放弃做某些菜来获得更大的总和。

示例 1:

输入:satisfaction = [-1,-8,0,5,-9]
输出:14
解释:去掉第二道和最后一道菜,最大的 like-time 系数和为 (-1*1 + 0*2 + 5*3 = 14) 。每道菜都需要花费 1 单位时间完成。

示例 2:

输入:satisfaction = [4,3,2]
输出:20
解释:可以按照任意顺序做菜 (2*1 + 3*2 + 4*3 = 20)

示例 3:

输入:satisfaction = [-1,-4,-5]
输出:0
解释:大家都不喜欢这些菜,所以不做任何菜就可以获得最大的 like-time 系数。
私货&废话

        首先还是看题意吧,这题难得的容易理解,出题人语文这次学的不错,我们不妨换个说法,这里我要加入点私货了,鬼泣各位都玩过吧,鬼泣系列除了华丽的搓招最灵魂的一点就是评分系统 了,从D打到SSS,这个评价可不只是说你当前的分数倍率,还是你打的好不好看的标志

        而从D到SSS实际上是7个评分倍率,结算的评分来自于你造成的伤害*当前倍率,你可以通过战斗提升倍率,在高倍率的加成下获得高分数(所以高手都喜欢在敌人丝血放大招吃伤害溢出分数)

        假如说每一次攻击都可以提升一个评价,即当前分数的倍率,比如D评价下我们的评分倍率只有1.0,攻击之后变成C,也就是说下一个攻击的评分倍率是2.0,依次往后分别是3.0一直到SSS的7.0,虽然每次攻击都能提升倍率,但每次攻击可以造成的伤害都不一样(我规定的),那么如果能使用的仅有5招,并且每一招只能使用一次,按照什么顺序出招才能获得最高分数?

        这是我们可用的所有招式的伤害值nums[-1,-8,0,5,-9],至于负数,就当是将所有攻击的伤害都减去一个同一个常数,你可以将伤害为负的攻击带入连招来给下一次攻击提升分数倍率,求可获取的最大分数,你也可以随意调整攻击顺序,也可以不使用其中某些招式

      

解法

        我们计算分数的时候,最好的结果一定是最大的数字加了最多次,也就是说最高的伤害*最高的倍率,其次是低一级的伤害乘以低一级的倍率,也可以把伤害为负数的攻击放在最前面,这样就能让它们乘以最小的倍率还能给下一次攻击提升倍率,但是如果负数过“大”,比如[ -2,1,2],纵使把-2放在第一位,结果是

-2*1+1*2+2*3=6

如果不带上-2得到的就是1*1+2*2=5

但如果第一个数是-10,结果就是

-10*1+1*2+2*3=-2

还不如不带,所以要考虑的就是要不要用一些小于0的数字换倍率

直接使用动态规划 

nums=[-1,-8,0,5,-9]

这里我是用的是用 f[ n ]来表示仅使用 n 个数字时能获得的最大值,如果n个数字全都是小于0的,就直接取0,如果只有一个数字,那么一定是最大值,但如果最大值也小于0,不如不要直接等于0,所以f [ 1 ]就是nums中的最大值和0的较大数,因为后面除了最大值还需要取第二大值和第三大值,最好的方法应该就是排序了,给数组排个序

Arrays.sort(nums)

f[ 1 ]  = max(nums[len-1],0)

然后f[ 2 ]就是当存在最多两个值时如何取最大值,f [ 2 ]的最大值就是  “最大的数*2+第二大的数*1”或者 f [ 1 ]

为什么候选是f [ 1 ]?因为如果第二大的数如果是个负数,可能会导致 “最大的数*2+第二大的数*1” 反而变得比f [ 1 ]还小,不如舍弃第二大的数,然后还是使用 第一大的数*1,也就是f [ 2-1 ]

        public int maxSatisfaction(int[] satisfaction) {
            int len = satisfaction.length;
            Arrays.sort(satisfaction);//首先给数组排序
            int[] f = new int[len+1];//因为我定义的f是指当前包含多少个数字时的结果
            //最后要求的结果就是f[最大长度]
            //而数组最大下标不能超过长度,所以数组要+1
            f[1] = Math.max(satisfaction[len-1],0);
            //如果最大值为负数f[1]还不如取0
            f[2] = Math.max(satisfaction[len-1]*2+satisfaction[len-2],f[1]);//如果你把这个代码提交了要注释掉这一行,有的数组最大只到f[1]
            //前两个数的最大值,如果 最大的数*2+第二大的数*2反而更小了,
            //不如不要第二大的数,还是用 最大的数*1,也就是f[2-1]
            int sum = satisfaction[len-1];
            int sum2 = satisfaction[len-1];
            //这里定义的sum和sum2都是最大的数,上面的f[1]和f[2]可以看到,
            //第一次是 最大的数字*1,第二次是 最大的数字*2+第二大的数字*1
            //然后第三次是 最大的数字*3+第二大的数字*2加第三大的数字*1
            //所以以此类推,实际上第一次是最大的一个数
            //然后第二次又加上了最大的前两个数
            //然后又是最大的前三个数
            for (int i = 2; i <= len; i++) {
                sum2+=satisfaction[len-i];//注意这里的len-i其实是len+1-i-1,len+1是数组f的长度,len+1-i表示倒数第i个值,这里的i是f的i,f的i又是表示i个数,所以i是从1开始,而sum2的初始值已经是nums[len-1]了,所以最后再减去1等于len-i
                //这里的sum2就是最大的前i个数字的和
                //当然每个数字只加了一次
                sum+=sum2;
                //所以sum就是把每次的sum2加起来
                //最后的结果就是上面的最大*3+第二*2+第三*1......
                f[i] = Math.max(sum,f[i-1]);
                //最后动态规划每次取sum和f[i-1],比较
            }
            return f[len];
        }

  • 42
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值