从零开始的算法竞赛入门教程:标准模板库
前言
标准模板库,简称STL,包含有大量的模板类和模板函数,是C升级到C++的++里面包括的部分,非常方便实用,是竞赛中的必备技能之一,当然STL和C++语法一样,想介绍详细,也会变成厚厚的一本书(《 STL源码剖析 》),所以这和语法的学习方法是一样的,不断学习需要用到的即可,从doc中学习最标准的书写方式,从别人的代码中学习一些奇技淫巧高效的使用方法
题意
[Greedy Gift Givers]
给定互送礼物的列表,确定每个人收到的礼物价值比送出的多多少
每个人都准备了一些钱来送礼物,而这些钱将会被平均分给那些将收到他的礼物的人
1 | 5 |
1 | dave 302 |
题目分析
当你第一次拿到这种题的时候,可能会觉得人名的处理非常的棘手
那么我们先来做一个题目简化,将这些人名全部替换成数字,那么这个问题就变成一个较为简单的模拟题了:读入每个代表人的数字(1到n),然后读入他准备用于买礼物的钱,准备送的朋友,计算每个朋友收到的礼物价值和这个人的花费,将花费计算并保存在每个人对应的值上(可以用一个数组来存储),送出去的钱(朋友收到的总和)计负,收到的钱计正相加处理,实现起来应该并不困难
现在每个人的标识从数字变为了字符串,我们需要额外处理的问题是,如何去定位这个人,原来的数字可以作为数组的下标,但是现在字符串不可以,这就是最大的问题,一个朴素的处理方式是,我们先用一个字符串数组保存下人名,即name[0]到name[n-1]保存了所有人的名字,然后用一个money数组来保存值的计算过程,每次处理到人名,我们就在name这个表中去查找,找到之后将其在name中的下标id作为人的标识符,将对应的值计算记录在money[id]上
在name表中查找的过程可以直接用for循环去找,这么处理的时间复杂度是$O(n^2)$的 (时间复杂度是什么,如何计算时间复杂度:搜索相关知识或阅读《算法导论》相关章节),用哈希表来处理这个过程则可以对复杂度进一步优化时间度到$O(n)$ (学有余力的同学可以自行学习哈希表如何处理此类问题)
接下来我将介绍如何使用STL中的模板函数来解决这个问题
知识点
输入输出流
首先,出于后续处理的考虑,字符串用string类来读入,而不是之前学到过的char数组,string是无法采用c式读入的,所以我们要用cin来处理读入
这里需要说明一下C++输入输出流在竞赛中通常是如何处理的,在C++中,cin和cout默认和stdio同步,即可以和C式读入混用而不会导致指针混乱,但是同步是有代价的,函数需要用到各自的缓冲区,所以速度就会变慢,那么如果想要cin的速度和scanf一样快,则需要取消缓冲区同步,可以简单地通过一行代码做到
1 | ios::sync_with_stdio(false); |
此外在默认情况下,cin会绑定cout,用于保证输入缓冲区在调用输入之前被刷新,为了进一步加速,我们可以将两者的绑定也取消
1 | cin.tie(NULL); // cout.tie(NULL); |
STL-vector
向量vector是STL中的一种容器,可以实现动态增长,也就是说,你在定义的时候不需要限定长度,当你往里放入元素的时候容量会自动变大
举个例子,我们定义一个int类型的向量,我们不需要定义其大小,直接里放入元素即可,已经被元素占据的位置可以直接通过下标访问
1 | vector<int> a; |
那么如果我希望直接在下标为5的位置直接放入这个3呢,那不是需要先往里塞入5个0才可以么?这种情况,我们就可以定义一下vector的长度,然后就可以当做数组一样用了
1 | vector<int> a(10); |
STL-map
映射map是STL中的一种容器,底层实现是红黑树,我们在之前分析题目的时候提到,这个题如果把名字从字符串改成编号,我们就可以用数组来存储信息以简单地解决这个问题,那么map在这个题中可以做的事情概括成一句话就是,让字符串可以作为下标
map可以做的事情就是将一种数据类型映射到另一种数据类型,我们知道,对于X集合到Y集合的单射f,有f(x)=y,其中x和y是两个集合中对应的元素。map呢,就是实现了这样一个f,你令f(x)=y,那么x为下标的f值就被指向y,且你可以随时更新f(x)的值,听起来是不是,其实f就像是一个数组
举个例子,将string映射到int
1 | map<string, int> mp; |
代码中name是字符串,而money是数字,也就是说,我们可以将字符串作为下标来建立一个类似数组的东西来处理值了,map的单次查找插入时间为$O(log(n))$,而另有一种STL:unordered_map,底层实现是哈希桶,处理方式和map类似,但是两者在不同的问题规模下各有优劣,同学们可以自行查阅资料学习
代码
1 |
|
Accept
任务清单
- 用最朴素的方式实现这道题(用数组存储名字,根据字符串查找)
- 学习并使用map(unordered_map)来完成这道题,了解map的更多用法
- 学习哈希表(开址法,拉链法(ASL更低)),用自己写的哈希表完成这道题
- 学习set(multiset),了解和map用法的区别,各自的优劣