STL 之 set 和 multiset

说实话,在竞赛中学好 STL 真的很重要,也就是我们说所的站在巨人的肩膀上。

set
set集合的很大特点是内部已经排好序了(默认从小到达),当然也可以自定义排序,并且有自动去重的功能。

set <int> a;         创建一个集合 a
a.insert();          插入数据
a.erase();           删除数据
a.clear();           清除所有元素
a.size();            统计set集合中元素的个数
a.empty();           判断集合是否为空
a.begin();           返回指向第一个元素的迭代器
a.end();             返回指向最后一个元素的迭代器
a.find();            查找指定元素,返回一个被查找元素的迭代器
a.lower_bound();     返回指向大于等于某个元素的值的第一个元素的迭代器
a.upper_bound();     返回指向大于某个元素的值的第一个元素的迭代器
a.count();           返回某个值元素的个数 

创建一个迭代器(map、vector的迭代器的定义都是如此)
set < int > :: iteritor it;
遍历 set 集合
for(it = a.begin(); it != a.end(); ++it) {
}
for(auto x:a)

所以 set 应该是一个十分好用的迭代器

multiset
multiset 集合和 set 集合可以说大同小异,最主要的区别在于multiset中的元素是没有去重的,其元素是可以重复的,但其内部也是依旧有序的。

multiset < int > a;
multiset < int > :: iteritor it;
遍历 multiset 集合
for(it = a.begin(); it != a.end(); ++it) {
}
上面均与 set 集合的用法一致,而且multiset的用法也是极为广泛的

我们来看一个例题:
题目链接: Knapsack Packing
大致题意就是给了最初的 n 个元素, 元素之间进行组合形成了2 ^n个元素,这里必然包含空集(0),但所给的2 ^n,元素可能出现错误,让你判断是否正确,如果是输出最初的 n 个元素,否则输出impossible。

解一
这个题目就需要用到 multiset ,先将所有元素放入 multiset 中,每次考虑最大和次大的,必能得到 n 个物品中其中一个物品的重量,再从 multiset 中删除与此重量有关的所有组合,否则输出impossible。

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
//关于n件物品的重量来满足这些组合 
int n, m;
int ans[25]; 
multiset <int> s; // 地址的形式存在  

int main()
{
     
	int x, cnt = 0;
	cin >> n;
	m = pow(2, n);
	for(int i = 0; i < m; ++i) cin >> x, s.insert(x);
	// 只需走 n 遍即可
	for(int i = 1; i <= n; ++i) {
     
		if(s.size() < 2) break; // s.size() == 1 才是正常的 
		++cnt;                  // 保证执行 n 遍 
		x = *prev(s.end()) - *prev(prev(s.end())); // 最大元素 - 次大元素, 形成一个差值 
		ans[i] = x; // 利用该差值, 删除与此重量有关的其它重量组合
		multiset <int>::iterator it, p; 
		for(it = s.begin(); it != s.end(); ++it) {
     
			int y = *it; // 迭代器
			p = s.upper_bound(x + y); // 大于 之后 
			p = prev(p); // 之前 
			if(p == s.end() || p == it || *p != x + y) {
      // 不存在(正常是必定存在的)
				cout << "impossible" << endl;
				return 0;
			}
			s.erase(p);  // 不一定都会执行, 集合 s 中有 p 才会进行删除 
		}
	} 
	sort(ans + 1, ans + 1 + n);
	if(s.size() != 1 || *s.begin() != 0 || cnt < n) cout << "impossible" << endl;
	else for(int i = 1; i <= n; ++i) cout << ans[i] << endl;
	return 0;
}

解二
先进行从小到达进行排序,最小一定为0(空集合),次小为集合中最小的数,使用次小的数进行组合产生一个新的数字,将其填入一个集合中,进行扫描删除即可。

#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 300000 + 5;
multiset <int> st, tmp;
map <int, int> mp;
vector <int> res;
int n, m, a[N];

int main()
{
     
	cin >> n;
	m = 1 << n; // 右移 n 位, m = 2^n 
	for(int i = 1; i <= m; ++i) cin >> a[i];
	sort(a + 1, a + 1 + m);
	if(a[1] != 0) {
     
		cout << "impossible" << endl;
		return 0;
	}
	int cnt = 0;
	for(int i = 2; i <= m; ++i) {
      // 从最小开始找 
		if(mp[a[i]]) {
     
            mp[a[i]]--;
			continue;	
		}
		else {
     
			++cnt;
			res.push_back(a[i]);
			for(auto x:st) tmp.insert(x + a[i]);
			for(auto x:tmp) st.insert(x), mp[x]++;
			st.insert(a[i]);
			tmp.clear();
		}
		if(cnt > n) {
     
			cout << "impossible" << endl;
			return 0;
		} 
	}
	sort(res.begin(), res.end());
	for(auto x:res) cout << x << endl;	 
	return 0;	
} 

set 集合很重要,一定要弄懂哦 ~~

你可能感兴趣的:(STL)