【备战秋招】每日一题:4月15日美团春招:题面+题目思路 + C++/python/js/Go/java带注释

2023大厂笔试模拟练习网站(含题解)
www.codefun2000.com
最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据,挂载到我们的OJ上,供大家学习交流,体会笔试难度。现已录入200+道互联网大厂模拟练习题,还在极速更新中。欢迎关注公众号“塔子哥学算法”获取最新消息。

提交链接:

https://codefun2000.com/p/P1138

为了更好的阅读体检,可以查看OJ上的题解。进入提交链接,点击右边菜单栏的"查看塔子哥的题解"

在线评测链接:P1238

题目内容

塔子哥是一个收藏家,他喜欢收集各种珍奇的物品。最近,他在旅游时在俄罗斯购买了一些俄罗斯套娃。他深深地被这些绚丽多彩的小玩意吸引住了,每天都会花费大量的时间玩弄它们。随着时间的推移,他逐渐发现将套娃放在其他套娃内是一个有趣的游戏,并决定挑战自己,看看他是否能以最小的成本套上所有的俄罗斯套娃。

具体的,塔子哥有 n 个俄罗斯套娃,第 i 个俄罗斯套娃的大小为 a_i,内部空间为 b_i ( b_i\le a_i )和一个价值 c_i

对于两个俄罗斯套娃 x 和 y , x 能放入 y 中当且仅当 a_x\le b_y,且放入后会占据 y 大小为 a_x 的内部空间,即y 的内部空间剩下b_y-a_x,每个俄罗斯套娃只能放在另外的一个俄罗斯套娃内,每个俄罗斯套娃内部也只能放一个俄罗斯套娃(当然内部放的这个俄罗斯套娃可以内部还有俄罗斯套娃)。

显然俄罗斯套娃是套的越多越好,如果套完之后俄罗斯套娃 i 还剩 k 的内部空间塔子哥需要付出 c_i\times k的花费,总花费为所有俄罗斯套娃的花费之和。

现在塔子哥想知道最小的花费为多少?

输入描述

第一行一个正整数 n ,表示俄罗斯套娃的个数

接下来三行每行 n 个整数,分别为

a1,a2,...,an
b1,b2,...,bn
c1,c2,...,cn

1\le n,a_i,b_i,c_i \le 100000b_i\le a_i

输出描述

输出一个整数表示最小的花费。

样例

输入

3
5 4 3
4 2 2
3 2 1

输出

6

样例解释

将第二个俄罗斯套娃放在第一个里面,第三个不动,这样第一个俄罗斯套娃剩 0 的空间,第二个剩 2 ,第三个剩 2 。总花费为 0*3+2*2+2*1=6 。

可以证明这是花费最小的方案。

思路

贪心 + multiset(平衡树)

每个物品中能放至多一个其他的物品,且每个物品只能放在至多一个物品内。

先考虑单位空间价值更大的,让他们内部的空间先被填充。

按照单位空间价值从大到小枚举每个物品,对每个物品找到一个体积小于当前枚举物品的空间的物品。即找到一个小于等于 x 的最大的 y 值。

因为物品的体积可能相同,故需要一个可以存储重复元素的 multiset,其中加入了所有的物品体积。

这里需要注意的是,一个物品的体积和空间可能相同,故我们需要判断获取的小于 x 的最大的 y 值是否可能和当前枚举的物品的空间相同:

  • 如果相同则可能是同一物品,故需要判断这个体积大小的物品的数量

    • 如果恰好只有一个,则说明是自己,则需要找到更小的

    • 如果不止一个,则说明存在其他的和当前枚举的物品空间大小相同的物品 i ,将物品 i 放入到当前物品中即可。

此外,我们每将一个物品放到另一个物品的空间中之后,就需要删除这个物品,防止多次使用,因此上述的重复判断需要有一个额外的标记,下面以 gt1 作为每个体积的物品数量是否大于 1 的标记。

时间复杂度:O(n\log n)

类似题目推荐

Codefun2000

set/multiset 练习题

  • 美团 P1269. 2023.04.29-春招-第四题-SSTF算法

贪心练习题

  • 荣耀 P1040. 2022.9.22-破译数字密码

  • 腾讯 P1005. 2022.10.16-汽车

LeetCode

set/multiset 练习题

  • 327. 区间和的个数

  • 218. 天际线问题

代码

CPP

#include 
using namespace std;
​
const int N = 100010;
struct Node {
    int a, b, c;
}q[N];
​
// 每个值的剩余数量
int cnt[N];
// 一个数的初始数量是否大于 1
bool gt1[N];
int n;
​
int main()
{
    long long ans = 0;
    multiset S;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &q[i].a);
        // 如果一个值 q[i].a 的总数量大于 1,则 gt1[q[i].a] = true
        if (++cnt[q[i].a] > 1) {
            gt1[q[i].a] = true;
        }
        S.insert(q[i].a);
    }
    for (int i = 1; i <= n; ++i) scanf("%d", &q[i].b);
    for (int i = 1; i <= n; ++i) {
        // 初始化所有的剩余空间都是满的
        scanf("%d", &q[i].c);
        ans += 1ll * q[i].c * q[i].b;
    }
​
    // 按照每个空间的单位价值从大到小排序,先处理单位价值更大的
    sort(q + 1, q + n + 1, [](const Node& A, const Node& B) {
        return A.c > B.c;
    });
​
    for (int i = 1; i <= n; ++i) {
        // 找到小于等于 q[i].b 的最大
        auto it = S.upper_bound(q[i].b);
        if (it == S.begin()) continue;
        it--;
​
        // 如果选择的物品的体积小于当前物品的空间
        // 或者选择的物品的体积等于当前物品的空间,但是这个空间的总数量大于 1
        if (*it != q[i].a || gt1[*it]) {
            ans -= 1ll * (*it) * q[i].c;
            S.erase(it);
        } else {
            // 否则就得继续往前找体积小于当前物品的空间的物品
            if (it == S.begin()) continue;
            it--;
            ans -= 1ll * (*it) * q[i].c;
            S.erase(it);
        }
    }
​
    printf("%lld\n", ans);
​
    return 0;
}

python

from sortedcontainers import SortedSet
​
N = 100010
# 每个值的剩余数量
cnt = [0] * N
# 一个数的初始数量是否大于 1
gt1 = [False] * N
​
S = SortedSet()
​
n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
c = list(map(int, input().split()))
​
ans = 0
for i in range(n):
    cnt[a[i]] += 1
    # 如果一个值 q[i].a 的总数量大于 1,则 gt1[q[i].a] = True
    if cnt[a[i]] > 1:
        gt1[a[i]] = True
    # 初始化所有的剩余空间都是满的
    ans += b[i] * c[i]
    S.add(a[i])
​
idx = [i for i in range(n)]
# 按照每个空间的单位价值从大到小排序,先处理单位价值更大的
idx.sort(key=lambda x: -c[x])
​
for i in range(n):
    j = idx[i]
    # 找到小于等于 q[i].b 的最大
    it = S.bisect_right(b[j])
    if it == 0: continue
    it -= 1
​
    val = S[it]
    # 如果选择的物品的体积小于当前物品的空间
    # 或者选择的物品的体积等于当前物品的空间,但是这个空间的总数量大于 1
    if val != a[j] or gt1[val]:
        ans -= val * c[j]
        S.remove(val)
    else:
        # 否则就得继续往前找体积小于当前物品的空间的物品
        if it == 0: continue
        it -= 1
        val = S[it]
        ans -= val * c[j]
        S.remove(val)
​
print(ans)

Java

import java.util.*;
​
public class Main {
    static final int N = 100010;
​
    static class Node {
        int a, b, c;
    }
​
    // 每个值的剩余数量
    static int[] cnt = new int[N];
    // 一个数的初始数量是否大于 1
    static boolean[] gt1 = new boolean[N];
    static int n;
​
    public static void main(String[] args) {
        long ans = 0;
        TreeMap S = new TreeMap<>();
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        Node[] q = new Node[n + 1];
        for (int i = 1; i <= n; ++i) {
            q[i] = new Node();
            q[i].a = scan.nextInt();
            // 如果一个值 q[i].a 的总数量大于 1,则 gt1[q[i].a] = true
            if (++cnt[q[i].a] > 1) {
                gt1[q[i].a] = true;
            }
            S.put(q[i].a, S.getOrDefault(q[i].a, 0) + 1);
        }
        for (int i = 1; i <= n; ++i) q[i].b = scan.nextInt();
        for (int i = 1; i <= n; ++i) {
            q[i].c = scan.nextInt();
            ans += (long) q[i].c * q[i].b;
        }
​
        // 按照每个空间的单位价值从大到小排序,先处理单位价值更大的
        Arrays.sort(q, 1, n + 1, new Comparator() {
            @Override
            public int compare(Node o1, Node o2) {
                return o2.c - o1.c;
            }
        });
​
        for (int i = 1; i <= n; ++i) {
            // 找到小于等于 q[i].b 的最大
            Integer key = S.floorKey(q[i].b);
            if (key == null) continue;
​
            // 如果选择的物品的体积小于当前物品的空间
            // 或者选择的物品的体积等于当前物品的空间,但是这个空间的总数量大于 1
            if (!key.equals(q[i].a) || gt1[key]) {
                ans -= (long) key * q[i].c;
                if (S.get(key) == 1) {
                    S.remove(key);
                } else {
                    S.put(key, S.get(key) - 1);
                }
            } else {
                // 否则就得继续往前找体积小于当前物品的空间的物品
                key = S.floorKey(key - 1);
                if (key == null) continue;
                ans -= (long) key * q[i].c;
                if (S.get(key) == 1) {
                    S.remove(key);
                } else {
                    S.put(key, S.get(key) - 1);
                }
            }
        }
​
        System.out.println(ans);
    }
}

Python、Golang 和 JavaScript 标准库中没有实现基于平衡树的数据结构,故如实在需要,还是需要 C++ 和 Java

你可能感兴趣的:(备战2023秋招,java,c++,开发语言,python,javascript)