多元集合multiset用法

本文介绍了一道趣味数学问题——如何通过合理安排果子的合并顺序以达到最小体力消耗的目标,并利用C++多元集合进行高效求解。通过对题目需求的解析及代码实现,展示了多元集合的基本操作及其在解决实际问题中的应用。
描述
小明很想吃果子,正好果园果子熟了。在果园里,小明已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。小明决定把所有的果子合成一堆。 因为小明比较懒,为了省力气,小明开始想点子了:每一次合并,小明可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了。小明在合并果子时总共消耗的体力等于每次合并所耗体力之和。因为还要花大力气把这些果子搬回家,所以小明在合并果子时要尽可能地节省体力。假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出合并的次序方案,使小明耗费的体力最少,并输出这个最小的体力耗费值。
例如有3种果子,数目依次为1,2,9。可以先将1、2堆合并,新堆数目为3,耗费体力为3。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为12,耗费体力为12。所以小明总共耗费体力=3+12=15。可以证明15为最小的体力耗费值。

输入
第一行输入整数N(0<N<=10)表示测试数据组数。接下来每组测试数据输入包括两行,第一行是一个整数n(1<=n<=12000),表示果子的种类数。第二行包含n个整数,用空格分隔,第i个整数ai(1<=ai<=20000)是第i种果子的数目。

输出
每组测试数据输出包括一行,这一行只包含一个整数,也就是最小的体力耗费值。输入数据保证这个值小于2^31。

样例输入
1
3
1 2 9

样例输出

15

代码如下:

#include<iostream> #include<algorithm> #include<cstdio> #include<string.h> #include<set> using namespace std; int main() { int ncase, fruit, num, i, sum; scanf("%d", &ncase); while(ncase--) { multiset<int> v; //可重复的多元集合 multiset<int>::iterator iter; sum = 0; scanf("%d", &fruit); for(i = 0; i < fruit; ++i) { scanf("%d", &num); v.insert(num); //插入一次自动排序一次 } while(v.size() != 1) { int tmp1, tmp2; tmp1 = *v.begin(); v.erase(v.begin()); tmp2 = *v.begin(); v.erase(v.begin()); sum += (tmp1 + tmp2); v.insert(tmp1 + tmp2); } printf("%d\n", sum); } return 0; }


C++ MultiSets(多元集合)
多元集合(MultiSets)和集合(Sets)相像,只不过支持重复对象。(具体用法请参照set容器)

函数列表:
begin()返回指向第一个元素的迭代器
clear()清除所有元素
count()返回指向某个值元素的个数
empty()如果集合为空,返回true
end()返回指向最后一个元素的迭代器
equal_range()返回集合中与给定值相等的上下限的两个迭代器
erase()删除集合中的元素
find()返回一个指向被查找到元素的迭代器
get_allocator()返回多元集合的分配器
insert()在集合中插入元素
key_comp()返回一个用于元素间值比较的函数
lower_bound()返回指向大于(或等于)某值的第一个元素的迭代器
max_size() 返回集合能容纳的元素的最大限值
rbegin()返回指向多元集合中最后一个元素的反向迭代器
rend()返回指向多元集合中第一个元素的反向迭代器
size()多元集合中元素的数目
swap()交换两个多元集合变量
upper_bound()返回一个大于某个值元素的迭代器
value_comp()返回一个用于比较元素间的值的函数

////////////////////////////////////////////////////////////////////////////////////
构造函数
explicit multiset(const Pred& comp = Pred(), const A& al = A());
multiset(const multiset& x);
multiset(const value_type *first, const value_type *last,
const Pred& comp = Pred(), const A& al = A());
 
begin
语法:
iterator begin();
返回指向当前集合中第一个元素的迭代器。
clear
语法:
void clear();
清除当前集合中的所有元素。
count
语法:
size_type count( const key_type &key );
返回当前集合中出现的某个值的元素的数目。
empty
语法:
bool empty();
如果当前多元集合为空,返回true;否则返回false。
end
语法:
iterator end();
返回指向当前集合中最后一个元素的迭代器。
equal_range
语法:
pair equal_range( const key_type &key );
返回集合中与给定值相等的上下限的两个迭代器。
erase
语法:
void erase( iterator pos );
void erase( iterator start, iterator end );
size_type erase( const key_type &key );
说明:
1. 删除i元素;
2. 删除从start开始到end结束的元素;
3. 删除等于key值的所有元素(返回被删除的元素的个数)。
find
iterator find( const key_type &key );
在当前集合中查找等于key值的元素,并返回指向该元素的迭代器;如果没有找到,返回指向多元集合最后一个元素的迭代器。
get_allocator
allocator_type get_allocator();
返回当前集合的分配器。
insert
语法:
iterator insert( iterator pos, const TYPE &val );
void insert( input_iterator start, input_iterator end );
pair insert( const TYPE &val );
The function insert() either:
1. 在迭代器i前插入val,并返回一个指向该元素的迭代器;
2. 将迭代器start开始到end结束返回内的元素插入到集合中;
3. 在当前集合中插入val元素,并返回指向该元素的迭代器和一个布尔值来说明val是否成功的被插入了。
key_comp
语法:
key_compare key_comp();
返回一个用于元素间值比较的函数对象。
lower_bound
语法:
iterator lower_bound( const key_type &key );
返回一个指向大于或者等于key值的第一个元素的迭代器。
max_size
语法:
size_type max_size();
返回当前多元集合能容纳元素的最大限值。
rbegin
语法:
reverse_iterator rbegin();
返回指向当前多元集合中最后一个元素的反向迭代器。
rend
reverse_iterator rend();
返回指向集合中第一个元素的反向迭代器。
size
语法:
size_type size();
返回当前多元集合中元素的数目。
swap
语法:
void swap( multiset &obj );
交换当前多元集合和obj多元集合中的元素。
upper_bound

语法:
iterator upper_bound( const key_type &key );
在当前多元集合中返回一个指向大于Key值的元素的迭代器
value_comp
语法:
value_compare value_comp();
返回一个用于比较元素间的值的函数对象。


1. std::vector —— 动态数组 ✅ 特点: 底层为连续内存的动态数组; 支持随机访问(O(1) 下标访问); 可以动态扩容(自动双倍增长); 是最常用、最灵活的容器。 ⏱ 时间复杂度: 操作 复杂度 访问元素 vec[i] O(1) 尾部插入/删除 push_back/pop_back 均摊 O(1) 中间插入/删除 O(n) 查找元素 O(n) 📌 使用场景: 替代普通数组,避免手动管理大小; 存图(邻接表)、存储输入数据、DP 数组等; 实现栈或队列(配合 push_back 和 pop_back); 💡 示例代码: 🔧 技巧: 使用 vec.reserve(n) 预分配空间,避免频繁扩容; vector<bool> 是特化版本,行为异常,慎用。 2. std::queue 和 std::priority_queue —— 队列与优先队列 ✅ std::queue 先进先出(FIFO)结构。 常用于 BFS、模拟任务调度等。 主要操作: 示例(BFS): ✅ std::priority_queue 默认是最大堆,顶部始终是最大元素; 常用于 Dijkstra、Huffman 编码、贪心算法等。 默认行为(大根堆): C++ std::priority_queue<int> pq; pq.push(3); pq.push(1); pq.push(4); std::cout << pq.top(); // 输出 4 小根堆写法C++ std::priority_queue<int, std::vector<int>, std::greater<int>> min_pq; 自定义比较(结构体): ⚠️ 注意:priority_queue 不支持修改已有元素,也不能遍历。 3. std::stack —— 栈 后进先出(LIFO),适合 DFS、括号匹配、表达式求值等。 常用操作: C++ s.push(x); s.pop(); // 无返回值 s.top(); // 返回栈顶 示例: 4. std::deque —— 双端队列 双向队列,可在头尾高效插入/删除; 支持随机访问; push_front, pop_front, push_back, pop_back 均为 O(1); vector 扩容时会失效迭代器,而 deque 更稳定。 使用场景: 单调队列(滑动窗口最大值); BFS 中需要从前或后插入的情况; 实现双端队列类问题。 示例: 5. std::set / std::multiset —— 有序集合 ✅ std::set 内部基于红黑树实现,自动排序且去重; 插入、删除、查找均为 O(log n); 不支持下标访问,但可用迭代器遍历。 常用操作: 使用场景: 动态维护一个有序唯一集合; 查询前驱/后继(lower_bound / upper_bound); 替代平衡树。 ✅ std::multiset 允许重复元素的 set; 同样有序,支持 insert, erase, find, lower_bound 等; 删除单个元素时使用 erase(s.find(x)) 防止全部删除; erase(x) 会删掉所有等于 x 的元素! 6. std::map / std::unordered_map —— 关联容器(键值对) ✅ std::map 键值对容器,按键有序排列(红黑树); 插入、查找、删除:O(log n); 键自动排序,可遍历得到有序结果。 示例: ✅ std::unordered_map 基于哈希表实现; 平均操作复杂度 O(1),最坏 O(n); 不保证顺序; 在竞赛中常用于快速映射(如字符串 → ID、计数等)。 示例: C++ std::unordered_map<std::string, int> cnt; cnt["apple"]++; cnt["banana"]++; ⚠️ 注意事项: 哈希碰撞可能导致被卡(尤其在线评测系统),可加随机扰动防 hack; 对自定义类型需提供 hash 函数,否则不支持。 7. std::array —— 固定大小数组(C++11 起) 类似内置数组,但更安全、支持 STL 操作; 大小在编译期确定; 性能极高,适用于固定长度的小数组。 示例: C++ std::array<int, 5> arr = {1, 2, 3, 4, 5}; for (int x : arr) std::cout << x << " "; 8. std::pair 和 std::tuple —— 组合类型 ✅ std::pair<T1, T2> 存储两个元素的组合; 常用于 map 键值、priority_queue 中带权值的节点; 支持字典序比较(<),便于排序。 示例: C++ std::pair<int, std::string> p = {3, "hello"}; std::vector<std::pair<int, int>> edges; edges.push_back({u, v}); ✅ std::tuple 多元组,可存多个不同类型元素; 使用 std::make_tuple, std::tie, std::get 操作。 C++ auto t = std::make_tuple(1, "abc", 3.14); int a; std::string b; double c; std::tie(a, b, c) = t; 9. 其他实用技巧与组合使用 场景 推荐容器组合 图的邻接表表示 vector<vector<int>> 或 vector<vector<pair<int, int>>>(带权) 并查集 vector<int> 存 parent 数组 字符串哈希映射 unordered_map<string, int> 滑动窗口最大值 deque<int> 实现单调队列 Top-K 问题 priority_queue<int>(小根堆维护 K 个最大) 动态排名查询 set<int> + distance(s.begin(), it)(但慢,可用 PBDS 替代) ❗常见陷阱提醒 错误 正确做法 map[key] 会创建默认值,可能改变结构 查找用 find() 避免插入 vector 迭代器在 push_back 后可能失效 使用索引或重新获取 erase(remove(...)) 忘记写法 vec.erase(std::remove(vec.begin(), vec.end(), x), vec.end()); priority_queue 无法遍历 若需遍历,改用 vector + make_heap 或排序 unordered_map 被哈希攻击 加随机种子或切换到 map 给出以上内容的对应代码,并且简要注释功能
最新发布
10-23
### C++ 中 `multiset` 结合 `auto` 关键字的用法C++11 及更高版本中引入了 `auto` 关键字,用于让编译器自动推导变量类型。这使得代码更加简洁易读,尤其是在处理复杂容器(如 `std::multiset`)时。 #### 使用示例 下面是一个简单的例子展示如何结合 `auto` 和 `std::multiset`: ```cpp #include <iostream> #include <set> int main() { std::multiset<int> ms = {4, 2, 8, 4, 2}; // 创建一个多集并初始化 // 使用 auto 遍历多集中的元素 for (auto it : ms) { std::cout << it << " "; } std::cout << "\n"; // 获取迭代器并使用 auto 推断其类型 auto it_begin = ms.begin(); // 自动推导为 std::multiset<int>::iterator auto it_end = ms.end(); // 输出第一个元素 if (it_begin != it_end) { std::cout << "First element: " << *it_begin << "\n"; } // 查找特定值并打印出现次数 int value_to_find = 4; auto count = ms.count(value_to_find); std::cout << "Count of " << value_to_find << ": " << count << "\n"; return 0; } ``` 上述代码展示了几个关键点: - 初始化一个 `std::multiset` 并存储一些整数值[^3]。 - 利用范围基于的 `for` 循环和 `auto` 来遍历集合中的所有元素[^4]。 - 使用 `auto` 定义迭代器来简化语法,而无需显式指定复杂的类型名称[^5]。 - 调用成员函数 `count()` 统计某个值在 `multiset` 中的重复次数[^6]。 通过这种方式可以显著减少冗长的模板声明,并使程序更易于维护。 #### 性能考虑 需要注意的是,在某些情况下过度依赖于 `auto` 可能会降低可读性和调试难度。因此建议仅当类型明显或者不重要时才采用此方法[^7]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值