#P1238. 第4题-俄罗斯套娃
-
1000ms
Tried: 189
Accepted: 35
Difficulty: 10
所属公司 :
美团
时间 :2023年4月15日
-
算法标签>贪心算法
第4题-俄罗斯套娃
题目思路
思路:贪心 + multiset(平衡树)
每个物品中能放至多一个其他的物品,且每个物品只能放在至多一个物品内。
先考虑单位空间价值更大的,让他们内部的空间先被填充。
按照单位空间价值从大到小枚举每个物品,对每个物品找到一个体积小于当前枚举物品的空间的物品。即找到一个小于等于 x 的最大的 y 值。
因为物品的体积可能相同,故需要一个可以存储重复元素的 multiset,其中加入了所有的物品体积。
这里需要注意的是,一个物品的体积和空间可能相同,故我们需要判断获取的小于 x 的最大的 y 值是否可能和当前枚举的物品的空间相同:
- 如果相同则可能是同一物品,故需要判断这个体积大小的物品的数量
- 如果恰好只有一个,则说明是自己,则需要找到更小的
- 如果不止一个,则说明存在其他的和当前枚举的物品空间大小相同的物品 i ,将物品 i 放入到当前物品中即可。
此外,我们每将一个物品放到另一个物品的空间中之后,就需要删除这个物品,防止多次使用,因此上述的重复判断需要有一个额外的标记,下面以 gt1 作为每个体积的物品数量是否大于 1 的标记。
时间复杂度:O(nlogn)
类似题目推荐
Codefun2000
set/multiset 练习题
贪心练习题
LeetCode
set/multiset 练习题
代码
C++
#include <bits/stdc++.h>
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<int> 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<Integer, Integer> 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<Node>() {
@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
题目内容
小美是一个收藏家,他喜欢收集各种珍奇的物品。最近,他在旅游时在俄罗斯购买了一些俄罗斯套娃。他深深地被这些绚丽多彩的小玩意吸引住了,每天都会花费大量的时间玩弄它们。随着时间的推移,他逐渐发现将套娃放在其他套娃内是一个有趣的游戏,并决定挑战自己,看看他是否能以最小的成本套上所有的俄罗斯套娃。
具体的,小美有 n 个俄罗斯套娃,第 i 个俄罗斯套娃的大小为 ai ,内部空间为 bi ( bi≤ai )和一个价值 ci 。
对于两个俄罗斯套娃 x 和 y , x 能放入 y 中当且仅当 ax≤by ,且放入后会占据 y 大小为 ax 的内部空间,即y 的内部空间剩下 by−ax ,每个俄罗斯套娃只能放在另外的一个俄罗斯套娃内,每个俄罗斯套娃内部也只能放一个俄罗斯套娃(当然内部放的这个俄罗斯套娃可以内部还有俄罗斯套娃)。
显然俄罗斯套娃是套的越多越好,如果套完之后俄罗斯套娃 i 还剩 k 的内部空间小美需要付出 ci×k 的花费,总花费为所有俄罗斯套娃的花费之和。
现在小美想知道最小的花费为多少?
输入描述
第一行一个正整数 n ,表示俄罗斯套娃的个数
接下来三行每行 n 个整数,分别为
a1,a2,...,an
b1,b2,...,bn
c1,c2,...,cn
1≤n,ai,bi,ci≤100000 , bi≤ai
输出描述
输出一个整数表示最小的花费。
样例
输入
3
5 4 3
4 2 2
3 2 1
输出
6
样例解释
将第二个俄罗斯套娃放在第一个里面,第三个不动,这样第一个俄罗斯套娃剩 0 的空间,第二个剩 2 ,第三个剩 2 。总花费为 0∗3+2∗2+2∗1=6 。
可以证明这是花费最小的方案。
Related
In following contests: