LeetCode:1402. 做菜顺序(C++)

目录

[1402. 做菜顺序](#1402. 做菜顺序)

题目描述:

实现代码与解析:

暴力

原理思路:

动态规划

原理思路:

贪心

原理思路:


1402. 做菜顺序

题目描述:

一个厨师收集了他 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 系数。

提示:

  • n == satisfaction.length
  • 1 <= n <= 500
  • -1000 <= satisfaction[i] <= 1000

实现代码与解析:

暴力

cpp 复制代码
class Solution {
public:
    int maxSatisfaction(vector<int>& satisfaction) {
        int n = satisfaction.size();
        sort(satisfaction.begin(), satisfaction.end());

        int res = 0;
        for (int i = 0; i < n; i++) {
            int cur = 0;
            int cnt = 1;
            for (int j = i; j < n; j++) {
                cur += cnt * satisfaction[j];
                cnt++;
            }
            res = max(res, cur);
        }
        return res;
    }
};

原理思路:

数据少,可以直接暴力。主要介绍动态规划的写法。

动态规划

cpp 复制代码
class Solution {
public:
    int maxSatisfaction(vector<int>& satisfaction) {
        int n = satisfaction.size();
        sort(satisfaction.begin(), satisfaction.end());
        vector<vector<int>> f(n + 1, vector<int>(n + 1, 0));

        int res = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                f[i][j] = f[i - 1][j - 1] + satisfaction[i - 1] * j; // i == j
                if (i < j) {
                    f[i][j] = max(f[i][j], f[i - 1][j]);
                }
            }
        }
        
        for (int i = 1; i <= n; i++) {
            res = max(f[n][i], res);
        }
        return res;
    }
};

原理思路:

转换为背包问题,由于前 i 个数,在数组中是包含下标0和之前的数,所以算的时候,f[i]其实算的是遍历到satisfaction[i - 1]的值,感觉这种写法很容易让人在思考时绕晕。

正常来说,在自己控制输入时(acm模式),把数据在下标1的位置开始输入,这样不仅可以避免考虑边界问题,还能简化代码。

dp数组含义:

f[i][j] 为 在前 i 个数 中选择 j 个数可以获取的最大值。

递推式:

f[i][j] = f[i - 1][j - 1] + satisfaction[i - 1] * j;i == j时,前 i 个 选择 j 个,说明当前这个必须选,那么前 i - 1个就选择了j - 1个。

**f[i][j] = max(f[i][j], f[i - 1][j]);**当 i < j 时,前 i 个 选择 j 个,前 i - 1 个 选择 j 个,选择第j个数的最大值与不选择第j个的最大值比较,取max。

为什么先排序?

如果把一个大的数放在前面,那么它能选择的满意度只能是小的,而把其放在后面,则可以用动态规划从满意度0一直选到大的满意度,这样才能考虑到最优的选择情况。体现在代码中就是:

cpp 复制代码
satisfaction[i - 1] * j

其实原理和01背包思想是一致的,多了个排序。只不过容量为n,每个数的质量相当于1而已。

之前写的背包详解。可以看看。

动态规划:0-1背包、完全背包问题 | 详细原理解释 | 代码及优化(C++)_c++完全背包问题 动态规划-CSDN博客

贪心

cpp 复制代码
class Solution {
public:
    int maxSatisfaction(vector<int>& satisfaction) {
        sort(satisfaction.begin(), satisfaction.end(), greater<int>());
        int cur = 0, res = 0;
        for (int t: satisfaction) {
            if (cur + t < 0) break;
            cur += t;
            res += cur;
        }
        return  res;
    }
};

原理思路:

贪心思路就是,不断选最大的,如果不加权的总和小于0了,肯定会变小,就不再选。严格的证明可以看力扣的证明。

其实自己带入一些样例就懂了,就是逆向选择,每次符合条件时,res加上cur,像是大于零的后缀和相加。

相关推荐
blasit41 分钟前
笔记:Qt C++建立子线程做一个socket TCP常连接通信
c++·qt·tcp/ip
AI软著研究员1 小时前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish1 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱2 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者18 小时前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮18 小时前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者19 小时前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考19 小时前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx1 天前
CART决策树基本原理
算法·机器学习
Wect1 天前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript