ACM竞赛常用STL(一)2

简介: ACM/ICPC 竞赛之STL--iterator 简介iterator(迭代器)是用于访问容器中元素的指示器,从这个意义上说,iterator(迭代器)相当于数据结构中所说的“遍历指针”,也可以把iterator(迭代器)看作是一种泛化的指针。STL 中关于iterator(迭代器)的实现是相当复杂的,这里我们暂时不去详细讨论关于iterator(迭代器)的实现和使用,而只对iterator(迭代器)做一点简单的介绍。

ACM/ICPC 竞赛之STL--iterator 简介

iterator(迭代器)是用于访问容器中元素的指示器,从这个意义上说,iterator(迭代器)相当于数据结构中所说的“遍历指针”,也可以把iterator(迭代器)看作是一种泛化的指针。STL 中关于iterator(迭代器)的实现是相当复杂的,这里我们暂时不去详细讨论关于iterator(迭代器)的实现和使用,而只对iterator(迭代器)做一点简单的介绍。


简单地说,STL 中有以下几类iterator(迭代器):


输入iterator(迭代器),在容器的连续区间内向前移动,可以读取容器内任意值;输出iterator(迭代器),把值写进它所指向的容器中;前向iterator(迭代器),读取队列中的值,并可以向前移动到下一位置(++p,p++);双向iterator(迭代器),读取队列中的值,并可以向前向后遍历容器;随机访问iterator(迭代器), 可以直接以下标方式对容器进行访问,vector 的iterator(迭代器)就是这种iterator(迭代器);流iterator(迭代器),可以直接输出、输入流中的值;每种STL 容器都有自己的iterator(迭代器)子类,下面先来看一段简单的示例代码:


#include <iostream>
#include <vector>
using namespace std;
main()
{
    vector<int> s;
    for (int i=0; i<10; i++) 
        s.push_back(i);
    for (vector<int>::iterator it=s.begin(); it!=s.end();it++)
        cout << *it << " ";
    cout << endl;
    return 1;
}


vector 的begin()和end()方法都会返回一个vector::iterator 对象,分别指向vector 的首元素位置和尾元素的下一个位置(我们可以称之为结束标志位置)。对一个iterator(迭代器)对象的使用与一个指针变量的使用极为相似,或者可以这样说,指针就是一个非常标准的iterator(迭代器)。再来看一段稍微特别一点的代码:

#include <iostream>
#include <vector>
using namespace std;
main()
{
    vector<int> s;
    for (int i=0; i<10; i++) 
        s.push_back(i);
    for (vector<int>::iterator it=s.begin(); it!=s.end();it++)
        cout << *it << " ";
    cout << endl;
    return 1;


这段代码中的copy 就是STL 中定义的一个模板函数,copy(s.begin(),s.end(), ostream_iterator<int>(cout, " "));的意思是将由s.begin()至s.end()(不含s.end())所指定的序列复制到标准输出流cout 中,用" "作为每个元素的间隔。也就是说,这句话的作用其实就是将表中的所有内容依次输出。iterator(迭代器)是STL 容器和算法之间的“胶合剂”,几乎所有的STL 算法都是通过容器的iterator(迭代器)来访问容器内容的。只有通过有效地运用iterator(迭代器),才能够有效地运用STL 强大的算法功能。


ACM/ICPC 竞赛之STL--string

字符串是程序中经常要表达和处理的数据,我们通常是采用字符数组或字符指针来表示字符串。STL 为我们提供了另一种使用起来更为便捷的字符串的表达方式:string。string 类的定义在头文件<string>中。string 类其实可以看作是一个字符的vector,vector 上的各种操作都可以适用于string,另外,string 类对象还支持字符串的拼合、转换等操作。下面先来看一个简单的例子:


#include <iostream>
#include <string>
using namespace std;
int main()
{
    string s = "Hello! ", name;
    cin >> name;
    s += name;
    s += '!';
    cout << s << endl;
    return 0;
}


再以题1064--Parencoding 为例,看一段用string 作为容器,实现由P


代码还原括号字符串的示例代码片段:


int m;
cin >> m; // P 编码的长度
string str; // 用来存放还原出来的括号字符串
int leftpa = 0; // 记录已出现的左括号的总数
for (int j=0; j<m; j++){
int p;
cin >> p;
for (int k=0; k<p-leftpa; k++) str += '(';
str += ')';
leftpa = p;
}


ACM/ICPC 竞赛之STL--stack/queue

stack(栈)和queue(队列)也是在程序设计中经常会用到的数据容器,STL为我们提供了方便的stack(栈)的queue(队列)的实现。39准确地说,STL 中的stack 和queue 不同于vector、list 等容器,而是对这些容器的重新包装。这里我们不去深入讨论STL 的stack 和queue 的实现细节,而是来了解一些他们的基本使用。


1、stack

stack 模板类的定义在<stack>头文件中。


stack 模板类需要两个模板参数,一个是元素类型,一个容器类型,但只有元


素类型是必要的,在不指定容器类型时,默认的容器类型为deque。


定义stack 对象的示例代码如下:


stack<int> s1;


stack<string> s2;


stack 的基本操作有:


入栈,如例:s.push(x);


出栈,如例:s.pop();注意,出栈操作只是删除栈顶元素,并不返回该元素。


访问栈顶,如例:s.top()


判断栈空,如例:s.empty(),当栈空时,返回true。


访问栈中的元素个数,如例:s.size()


下面是用string 和stack 写的解题1064--Parencoding 的程序。


#include <iostream>
#include <string>
#include <stack>
using namespace std;
int main()
{
    int n;
    cin >> n;
    for (int i=0; i<n; i++)
    {
        int m;
        cin >> m;
        string str;
        int leftpa = 0;
        for (int j=0; j<m; j++) // 读入P 编码,构造括号字符串
        {  
            int p;
            cin >> p;
            for (int k=0; k<p-leftpa; k++) 
                str += '(';
                str += ')';
                leftpa = p;
        }
        stack<int> s;
        for (string::iterator it=str.begin();it!=str.end(); it++) 
        { // 构造M 编码
            if (*it=='(') s.push(1);
            else
            {
                int p = s.top(); s.pop();
                cout << p << " ";
                if (!s.empty()) s.top() += p;
            }
        }
        cout << endl;
    }
    return 0;


2、queue

queue 模板类的定义在<queue>头文件中。stack 模板类很相似,queue 模板类也需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deque 类型。


定义queue 对象的示例代码如下:


queue<int> q1;


queue<double> q2;


queue 的基本操作有:


入队,如例:q.push(x); 将x 接到队列的末端。


出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。


访问队首元素,如例:q.front(),即最早被压入队列的元素。


访问队尾元素,如例:q.back(),即最后被压入队列的元素。


判断队列空,如例:q.empty(),当队列空时,返回true。


访问队列中的元素个数,如例:q.size()


3、priority_queue

在<queue>头文件中,还定义了另一个非常有用的模板类priority_queue(优先队列)。优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序)。priority_queue 模板类有三个模板参数,第一个是元素类型,第二个容器类型,第三个是比较算子。其中后两个都可以省略,默认容器为vector,默认算子为less,即小的往前排,大的往后排(出队时序列尾的元素出队)。


定义priority_queue 对象的示例代码如下:


priority_queue<int> q1;


priority_queue< pair<int, int> > q2; // 注意在两个尖括号之间一定要留空格。


priority_queue<int, vector<int>, greater<int> > q3; // 定义小的先出队


priority_queue 的基本操作与queue 相同。


初学者在使用priority_queue 时,最困难的可能就是如何定义比较算子了。如果是基本数据类型,或已定义了比较运算符的类,可以直接用STL 的less算子和greater 算子——默认为使用less 算子,即小的往前排,大的先出队。如果要定义自己的比较算子,方法有多种,这里介绍其中的一种:重载比较运算符。优先队列试图将两个元素x 和y 代入比较运算符(对less 算子,调用x<y,对greater 算子,调用x>y),若结果为真,则x 排在y 前面,y 将先于x 出队,反之,则将y 排在x 前面,x 将先出队。


看下面这个简单的示例:


#include <iostream>
#include <queue>
using namespace std;
class T
{
    public:
        int x, y, z;
        T(int a, int b, int c):x(a), y(b), z(c){}
};
bool operator < (const T &t1, const T &t2)
{
    return t1.z < t2.z; // 按照z 的顺序来决定t1 和t2 的顺序
}
int main()
{
    priority_queue<T> q;
    q.push(T(4,4,3));
    q.push(T(2,2,5));
    q.push(T(1,5,4));
    q.push(T(3,3,6));
    while (!q.empty())
    {
        T t = q.top(); q.pop();
        cout << t.x << " " << t.y << " " << t.z << endl;
    }
return 0; 
}

/*输出结果为(注意是按照z 的顺序从大到小出队的):

3 3 6
2 2 5
1 5 4
4 4 3*/

//再看一个按照z 的顺序从小到大出队的例子:

#include <iostream>
#include <queue>
using namespace std;
class T
{
    public:
        int x, y, z;
    T(int a, int b, int c):x(a), y(b), z(c)
    {
    }
};
bool operator > (const T &t1, const T &t2)
{
    return t1.z > t2.z;
}
int main()
{
    priority_queue<T, vector<T>, greater<T> > q;
    q.push(T(4,4,3));
    q.push(T(2,2,5));
    q.push(T(1,5,4));
    q.push(T(3,3,6));
    while (!q.empty())
    {
        T t = q.top(); q.pop();
        cout << t.x << " " << t.y << " " << t.z << endl;
    }
    return 0;
}


输出结果为:


4 4 3


1 5 4


2 2 5


3 3 6


如果我们把第一个例子中的比较运算符重载为:


bool operator < (const T &t1, const T &t2){
return t1.z > t2.z; // 按照z 的顺序来决定t1 和t2 的顺序
}


则第一个例子的程序会得到和第二个例子的程序相同的输出结果。


再回顾一下用优先队列实现的题1067--Ugly Numbers 的代码:

#include <iostream>
#include <queue>
using namespace std;
typedef pair<unsigned long int, int> node_type;
int main( int argc, char *argv[] )
{
    unsigned long int result[1500];
    priority_queue< node_type, vector<node_type>,
    greater<node_type> > Q;
    Q.push( make_pair(1, 3) );
    for (int i=0; i<1500; i++)
    {
        node_type node = Q.top();
        Q.pop();
        switch(node.second)
        {
            case 3: Q.push( make_pair(node.first*2, 3) );
            case 2: Q.push( make_pair(node.first*3, 2) );
            case 1: Q.push( make_pair(node.first*5, 1) );
        }
        result[i] = node.first;
    }
    int n;
    cin >> n;
    while (n>0)
    {
        cout << result[n-1] << endl;
        cin >> n;
    }
    return 1;
}


ACM/ICPC 竞赛之STL--map

在STL 的头文件<map>中定义了模板类map 和multimap,用有序二叉树来存贮类型为pair<const Key, T>的元素对序列。序列中的元素以const Key部分作为标识,map 中所有元素的Key 值都必须是唯一的,multimap 则允许有重复的Key 值。可以将map 看作是由Key 标识元素的元素集合,这类容器也被称为“关联容器”,可以通过一个Key 值来快速确定一个元素,因此非常适合于需要按照Key值查找元素的容器。map 模板类需要四个模板参数,第一个是键值类型,第二个是元素类型,第三个是比较算子,第四个是分配器类型。其中键值类型和元素类型是必要的。map 的基本操作有:


1、定义map 对象,例如:


map<string, int> m;


2、向map 中插入元素对,有多种方法,例如:


m[key] = value;


[key]操作是map 很有特色的操作,如果在map 中存在键值为key 的元素对,


则返回该元素对的值域部分,否则将会创建一个键值为key 的元素对,值域为默认值。所以可以用该操作向map 中插入元素对或修改已经存在的元素对的值域部分。


m.insert( make_pair(key, value) );


也可以直接调用insert 方法插入元素对,insert 操作会返回一个pair,当map 中没有与key 相匹配的键值时,其first 是指向插入元素对的迭代器,其second 为true;若map 中已经存在与key 相等的键值时,其first 是指向该元素对的迭代器,second 为false。


3、查找元素对,例如:


int i = m[key];


要注意的是,当与该键值相匹配的元素对不存在时,会创建键值为key 的元素对。map<string, int>::iterator it = m.find(key);如果map 中存在与key 相匹配的键值时,find 操作将返回指向该元素对的迭代器,否则,返回的迭代器等于map 的end()(参见vector 中提到的begin


和end 操作)。


4、删除元素对,例如:


m.erase(key);删除与指定key 键值相匹配的元素对,并返回被删除的元素的个数。


m.erase(it);删除由迭代器it 所指定的元素对,并返回指向下一个元素对的迭代器。


看一段简单的示例代码:


#include<map>
#include<iostream>
using namespace std;
typedef map<int, string, less<int> > M_TYPE;
typedef M_TYPE::iterator M_IT;
typedef M_TYPE::const_iterator M_CIT;
int main()
{
    M_TYPE MyTestMap;
    MyTestMap[3] = "No.3";
    MyTestMap[5] = "No.5";
    MyTestMap[1] = "No.1";
    MyTestMap[2] = "No.2";
    MyTestMap[4] = "No.4";
    M_IT it_stop = MyTestMap.find(2);
    cout << "MyTestMap[2] = " << it_stop->second << endl;
    it_stop->second = "No.2 After modification";
    cout << "MyTestMap[2] = " << it_stop->second << endl;
    cout << "Map contents : " << endl;
    for(M_CIT it = MyTestMap.begin(); it != MyTestMap.end();it++)
    {
        cout << it->second << endl;
    }
    return 0;
}

/*程序执行的输出结果为:

MyTestMap[2] = No.2
MyTestMap[2] = No.2 After modification
Map contents :
No.1
No.2 After modification
No.3
No.4
No.5*/


再看一段简单的示例代码:


#include <iostream>
#include <map>
using namespace std;
int main()
{
    map<string, int> m;
    m["one"] = 1;
    m["two"] = 2;
    // 几种不同的insert 调用方法
    m.insert(make_pair("three", 3));
    m.insert(map<string, int>::value_type("four", 4));
    m.insert(pair<string, int>("five", 5));
    string key;
    while (cin>>key)
    {
        map<string, int>::iterator it = m.find(key);
        if (it==m.end())
        {
            cout << "No such key!" << endl;
        }
        else
        {
            cout << key << " is " << it->second << endl;
            cout << "Erased " << m.erase(key) << endl;
        }
    }
    return 0;
}


由于STL--algorithm较长  另加一篇详解

目录
相关文章
|
C++ 容器
ACM竞赛常用STL(二)之STL--algorithm
内部实现: 数组 // 就是没有固定大小的数组,vector 直接翻译是向量vector // T 就是数据类型,Alloc 是关于内存的一个什么东西,一般是使用默认参数。
65 0
|
5月前
|
存储
【博士每天一篇论文-理论分析】Dynamical systems, attractors, and neural circuits
本文是2016年Paul Miller在《F1000Research》上发表的论文,深入探讨了神经回路中的动力系统和吸引子,强调了使用基于动力系统的数学模型对神经回路进行准确建模的重要性,并分析了点吸引子、多稳态、记忆、抑制稳定网络等不同动力学系统在神经回路中的作用及对认知功能的影响。
30 7
【博士每天一篇论文-理论分析】Dynamical systems, attractors, and neural circuits
|
Java C语言 C++
ACM刷题之路(二)谈谈我对ACM的理解
ACM刷题之路(二)谈谈我对ACM的理解
128 0
|
存储 对象存储 C++
ACM竞赛常用STL(一)1
s.resize(n, val)改变序列的长度,超出的元素将会被删除,如果序列需要扩展(原空间小于n),将用val 填满扩展出的空间。
92 0
|
Java Android开发
ACM刷题之路(七)字符串处理 记元培ACM院赛
ACM刷题之路(七)字符串处理 记元培ACM院赛
102 0
|
机器学习/深度学习 人工智能 算法
295页博士论文探索强化学习抽象理论,获AAAI/ACM SIGAI博士论文奖提名
295页博士论文探索强化学习抽象理论,获AAAI/ACM SIGAI博士论文奖提名
149 0
ACM 选手带你玩转 KMP 算法!
ACM 选手带你玩转 KMP 算法!
ACM 选手带你玩转 KMP 算法!
|
算法 搜索推荐
ACM 选手带你玩转分治算法!
ACM 选手带你玩转分治算法!
ACM 选手带你玩转分治算法!
|
存储 算法 数据库管理
ACM 选手带你玩转二叉树
ACM 选手带你玩转二叉树
ACM 选手带你玩转二叉树
|
存储 算法 Java
大学生竞赛指南 ACM/ICPC
ICPC赛事由各大洲区域赛(Regional)和全球总决赛(WorldFinal)两个主要阶段组成。根据各赛区规则,每站前若干名的学校获得参加全球总决赛的资格,决赛安排在每年的4-6月举行,而区域赛一般安排在上一年的9-12月举行。一个大学可以有多支队伍参加区域预赛,但只能有一支队伍参加全球总决赛。
333 0

热门文章

最新文章