刷题小记3 | 豆包MarsCode AI 刷题

68 阅读5分钟

最大矩形面积

问题描述

对于一个有 N 个元素的数组,包含如下的元素 h1, h2, ..., hn,对于 k 个相邻的元素,我们定义它的最大面积如下:
R(k)=k∗min(h[i],h[i+1],....,h[i+k−1])R(k)=k∗min(h[i],h[i+1],....,h[i+k−1])

求 R(k) 的最大值

输入格式

总共有两行,第一行是数组长度 N,第二个是空格分割的所有数组的内容

输出格式

输出 R(k) 的最大值

输入样例

5
1 2 3 4 5

输出样例

9

数据范围

  • 1 <= N <= 10^5
  • 1 <= h[i] <= 10^6

思路

简单粗暴法

用一个三重循环来挨个找出最大的结果 时间复杂度为O(n^3),非常暴力。 只用暴力解法就没啥用意义了。

#include <iostream>
#include <vector>

int solution1(int n, std::vector<int> A) {
  // Edit your code here
  int res = 0;
  for (int i = 1; i < n + 1; i++) {
    // int min_height = 999;
    for (int j = 0; j < n; j++) {
      int min_height = 999;
      if (j + i > n) {
        break;
      }
      for (int k = j; k <= j + i - 1; k++) {

        if (A[k] < min_height) {
          min_height = A[k];
        }
      }
      std::cout << i << " " << min_height << std::endl;
      if ((i)*min_height > res) {
        res = i * min_height;
      }
    }
  }
  std::cout << res << std::endl;
  ;
  return res;
}

int main() {
  // Add your test cases here
  std::vector<int> A_case1 = std::vector<int>{1, 2, 3, 4, 5};
  std::cout << (solution(5, A_case1) == 9) << std::endl;
  return 0;
}

优化后的方法

首先一定要理解,为什么这道题叫做最大矩形面积。

给定一个数组 𝐴,我们需要找到矩形面积的最大值。矩形的面积取决于:

  • 柱子的高度(𝐴[𝑖])。
  • 矩形的宽度(由左右边界决定)。

对于每个柱子 𝐴[𝑖],宽度可以通过找到柱子 𝑖 左右两边比它矮的柱子的位置来确定。具体来说:

  • 左边界是𝑖 左侧第一个比 𝐴[𝑖] 小的元素的索引。
  • 右边界是 𝑖 右侧第一个比 𝐴[𝑖]小的元素的索引。

那么问题就变化为了求一个A[i]为长的矩形面积时,要找到最大的宽。这个宽度就应当是A[i]为中心,向两边扩散,扩散到出现比他小的数为止。这样就保证了A[i]在当前宽度内是最小的。这样才能计算以A[i]为长的面积。最终获得每一个以A[i]为长的矩形面积,找出最大的那个即可。

#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>

int solution(int n, std::vector<int> A) {
    // 初始化左右边界
    std::vector<int> left(n), right(n);
    std::stack<int> stk;

    // 找到每个元素的左边界
    for (int i = 0; i < n; ++i) {
        while (!stk.empty() && A[stk.top()] >= A[i]) {
            stk.pop();
        }
        left[i] = stk.empty() ? -1 : stk.top();
        stk.push(i);
    }

    // 清空栈,用于计算右边界
    while (!stk.empty()) stk.pop();

    // 找到每个元素的右边界
    for (int i = n - 1; i >= 0; --i) {
        while (!stk.empty() && A[stk.top()] >= A[i]) {
            stk.pop();
        }
        right[i] = stk.empty() ? n : stk.top();
        stk.push(i);
    }

    // 计算每个柱子作为高度时的最大面积
    int max_area = 0;
    for (int i = 0; i < n; ++i) {
        int width = right[i] - left[i] - 1;
        max_area = std::max(max_area, A[i] * width);
    }

    return max_area;
}

蛋糕工厂产能规划

问题描述

小明的蛋糕工厂每天可以生产的蛋糕数量是由工厂中的机器和工人的数量决定的,即 ( m * w )。现在他收到了一个大订单,需要尽快生产出 ( n ) 个蛋糕。为了提升生产速度,小明可以使用每天生产的蛋糕去购买额外的机器或工人,每台机器或每个工人的成本是 ( p ) 个蛋糕。

例如,如果工厂起始时有 1 台机器和 2 个工人,每次扩大产能的成本是 1 个蛋糕,为了生产 60 个蛋糕,小明可以这样操作:

  • 第一天:生产 2 个蛋糕,买入 2 台机器(总机器数变为 3)。
  • 第二天:生产 6 个蛋糕,买入 3 台机器,3 个工人(机器数 6,工人数 5)。
  • 第三天:生产 30 个蛋糕。
  • 第四天:再生产 30 个蛋糕,完成订单。

你的任务是帮助小明计算最快多少天能完成订单。


测试样例

样例1:

输入:m = 3, w = 1, p = 2, n = 12
输出:3

样例2:

输入:m = 10, w = 5, p = 30, n = 500
输出:8

样例3:

输入:m = 3, w = 5, p = 30, n = 320
输出:14

思路

大的思路

采用二分的方法去找到最小的天数。

比如k天能够完成生产,那么就在left-k内再去寻找能够完成订单的更小的天数。

如果k天不可以的话,那么就在k到right再去寻找能够完成订单的天数。

最终找到最小的k即可。

这样就变成了一个新的问题,就是k天内能否完成生产任务。

小的思路

现在就是要去确认k天能否完成订单。这里有个容易被题目描述带偏的地方在于是否要去判断要不要加员工或者机器,因为如果一直加机器和人,那么可能就得很长时间才能完成订单。但是其实很难说设计一个具体的条件来判断当日要不要加机器或人。

换个思路考虑,不需要在每天考虑是不是要加机器或人,只需要考虑今天加人或机器之前或之后的产量在剩余天数内能否完成任务。如果可以,那么就直接结束即可,这样就类似于计算后续天数不需要加机器或人所能得到的蛋糕产量。如果不行的话,下一天再加工人或者机器。

至于加机器或者加人的策略。可以选择将全部的蛋糕都尽可能的用来加人或机器。在选择加人或机器时,可以优先加数量较少的一方,这样带来的收益会更高一些。例如,机器10,人5。加一个机器55,加一个人60。

具体实现

def solution(m, w, p, n):
    def check(k, m, w, p, n):
        cakes = 0
        while k > 0:
            cakes += m * w
            if cakes >= n:
                return True
            while cakes >= p:
                if m > w:
                    w += 1
                else:
                    m += 1
                cakes -= p
            
            if cakes  + (m * w *(k-1))>= n:
                return True
            k -= 1
        return cakes >= n
    left = 0
    right = n // (m * w) + 1
    while left < right:
        #print(left,right)
        mid = (right - 1 + left) // 2
        
        if(check(mid,m,w,p,n)):
            right = mid
        else:
            left = mid + 1

    return left
if __name__ == "__main__":
    #  You can add more test cases here
    print(solution(3, 1, 2, 12) == 3 )
    print(solution(10, 5, 30, 500) == 8 )
    print(solution(3, 5, 30, 320) == 14 )