快乐时间最大化 | 豆包MarsCode AI刷题

12 阅读4分钟

题目

问题描述

小C喜欢在电子书城阅读书籍,并希望获得尽可能多的满足感。每本书的满意程度books[i]表示小C阅读这本书的评分。如果按照顺序阅读书籍,time[i]定义为阅读第i本书及之前所有书所花费的时间,即第i本书的快乐时间系数为time[i] * books[i]。小C可以按照任意顺序调整书籍的阅读顺序,以获得最大的【快乐时间】总和。

例如,给定的书籍评分是 [4, 3, 2],如果按评分升序排序来安排阅读,则小C可以获得最大的快乐时间系数。


测试样例

样例1:

输入:books = [4, 3, 2] 输出:20 说明:选择第一天读第三本书,第二天读第二本书,第三天读第一本书,快乐时间为20,可以证明,没有其他方案获得的快乐时间比此方案更大

样例2:

输入:books = [-1, -4, -5] 输出:0 说明:无论怎么读,最后的快乐时间均为负数,所以选择不读,快乐时间为0

样例3:

输入:books = [-1, -8, 0, 5, -9] 输出:14 说明:选择第一天读第一本书,第二天读第三本书,第三天读第四本书,快乐时间为14,可以证明,没有其他方案获得的快乐时间比此方案更大

解析

规则:以时间天数为倍率,假设当天是第t天,则有某本书能获得的快乐 = 书籍评分books[i] * t。那么我们想要获得最大的快乐时间系数,需亚尽可能地安排评分高的书籍在后面读,这样能获得最大的倍率。因此我们可以确定我们需要将书籍按照升序排列。

我们分析样例入手这个题目

都是非负数

最简单的情况,直接升序排序按倍率求和即可。

都是负数

不予考虑,看了还伤心。

有正有负

如果我们只考虑非负数,那么答案是(1 * 0) + (2 * 5) = 10

但是如果阅读一本负数的书反而答案更大(1 * (-1)) + (2 * 0) + (3 * 5) = 14

思考步骤:

首先按升序排列

 -9 -8 -1 0 5

因为非负数是一定要考虑的,因此我们找到非负数开始的位置pos,在这个样例中pos = 3,我们将非负数的计算放入答案sum = (1 * 0) + (2 * 5) = 10

接下来我们考虑添加负数,如果是添加负数的话那肯定是插入原来的式子的最前面,但是这样会导致我们原先的式子倍率要发生变化,该怎么变化呢?

假如我们在最前面插入一本书,那么原来的(1 * 0) + (2 * 5) 要变成(2 * 0) + (3 * 5)再加上新书book[i]。我们可以观察到,旧书的变化量为0 + 5,即每次都要增加一份旧书评分的总和,因此我们维护一个变量sigleSum,用来记录每次看过的书的评分总和,每次挑选剩余书籍里面的评分最大值的书,只要sigleSum的增量大于每次负数评分的减量,就能使的总快乐增加。

Code

 #include <iostream>
 #include <vector>
 #include <string>
 #include <algorithm>
 
 using namespace std;
 
 int solution(std::vector<int>& books) {
     int n = books.size();
     sort(books.begin(), books.end());
     
     int pos = 0;
     for (;pos < n; pos++) 
         if (books[pos] >= 0) break;
     
     int singleSum = 0, sum = 0;
     for (int i = pos; i < n; i++) {
         singleSum += books[i];
         sum += (i - pos + 1) * books[i];
     }
 
     for (int i = pos-1; i >= 0; i--) {
         if (singleSum + books[i] > 0) {
             sum += singleSum + books[i];
             singleSum += books[i];
         }
         else break;
     }
 
     return sum;
 }
 
 int main() {
     std::vector<int> books1 = {4, 3, 2};
     std::cout << (solution(books1) == 20) << std::endl;
 
     std::vector<int> books2 = {-1, -4, -5};
     std::cout << (solution(books2) == 0) << std::endl;
 
     std::vector<int> books3 = {-1, -8, 0, 5, -9};
     std::cout << (solution(books3) == 14) << std::endl;
 
     return 0;
 }

复杂度

时间复杂度分析

  1. 排序操作:首先我们对输入数组 books 进行排序。排序的时间复杂度为 O(n log n),其中 n 是书籍数量。
  2. 遍历数组:接下来,我们需要遍历书籍列表来计算快乐时间,并检查是否可以通过调整负书籍的位置来增加总快乐时间。这个过程的时间复杂度是 O(n),因为我们只需要遍历一次数组。

因此,整体的时间复杂度是排序操作和遍历操作的时间复杂度之和:

  • 排序:O(n log n)
  • 遍历:O(n)

所以,总时间复杂度为 O(n log n)

空间复杂度分析

  1. 额外空间:我们只用了几个常量变量来保存总快乐时间、累积的评分总和等,并没有使用额外的辅助空间,因此空间复杂度为 O(1)
  2. 排序空间:由于排序操作是原地排序(使用的是 std::sort),所以不需要额外的空间。

因此,总空间复杂度为 O(1)