zoukankan      html  css  js  c++  java
  • 浅谈 STL

    简介

    STL是Standard Template Library的简称,中文名标准模板库,从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合。这里的“容器”和算法的集合指的是世界上很多聪明人很多年的杰作。STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件。STL是C++的一部分,因此不用安装额外的库文件。

    ---------来自《百度百科》


    那就直接开讲了呐

    1.queue 队列

    头文件:

    #include <queue>
    using namespace std;
    

    简介:先进先出的无限长度数组。。。

    基本操作函数:

    • q.push():从队尾入队
    • q.pop():从对头出队
    • q.front():队头元素
    • q.back():队尾元素
    • q.empty:判断是否为空
    • q.size():求队列长度

    题目:Blah 数集

    数学家高斯小时候偶然间发现一种有趣的自然数集合 Blah。对于以 a 为基的集合 Blah 定义如下:

    1)a 是集合 Blah 的基,且 a 是 Blah 的第一个元素;
    2)如果 x 在集合 Blah 中,则 2x+1 和 3x+1 也都在集合 Blah 中;
    3)没有其他元素在集合 Blah 中了。
    现在小高斯想知道如果将集合 Blah 中元素按照升序排列,第 n 个元素会是多少?注意:集合中没有重复的元素。

    • 输入格式
      一行两个正整数,分别表示集合的基 a 以及所求元素序号 n,1≤a≤50,1≤n≤1000000。

    • 输出格式
      一行一个正整数,表示集合 Blah 的第 n 个元素值。

    样例输入

    28 5437
    

    样例输出

    900585
    

    分析
    这是一道很显然的一道队列题目,重点是熟悉队列的操作

    开两个队列,在每次入队后,找出两个队列队头较小的那一个,作为下一次入队的依据

    因为是升序,所以题目就是要求第 n 小的,也就是我们第 n-1 次操作找到的两个队列队头较小的哪一个

    AC代码

    #include <cstdio>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    queue<int> x; // 2a + 1
    queue<int> y; // 3a + 1
    
    int main() {
    	int a, n;
    	scanf("%d %d", &a, &n);
    	for(int i = 1; i < n; i++) {
            x.push(a * 2 + 1); // 分别入队
            y.push(a * 3 + 1);
            if(x.front() < y.front()) { // 取出两个序列的队头元素,进行比较
                a = x.front();
                x.pop(); // 先查找,再删除队头元素
            }
            else if(x.front() > y.front()) {
                a = y.front();
                y.pop();
            }
            else { // 如果相等,两边都要删除队头元素
                a = x.front();
                x.pop();
                y.pop();
            }
    	}
    	printf("%d", a);
    	return 0;
    }
    

    2.priority_queue 优先队列

    头文件:

    #include <queue>
    using namespace std;
    

    简介:保证队头一定是最大值,或队头一定是最小值的队列,内部由二叉堆实现

    基本操作函数:

    • q.push():从队尾入队
    • q.pop():从对头出队
    • q.top():队头元素
    • q.empty:判断是否为空
    • q.size():求队列长度

    特别注意:优先队列默认大根堆,如果要改为小根堆,需要建立自定义结构体,然后重载运算符

    struct node {
    	int id, v;
    	bool operator<(const node x) const {return v > x.v;}
    };
    priority_queue <node> q;
    

    当然也可以写成

    priority_queue<int, vector<int>, greater<int> > q;
    

    题目:有序表的最小和

    题目描述
    给出两个长度为 n 的有序表 A 和 B,在 A 和 B 中各任取一个元素,可以得到 n*n 个和,求这些和中最小的 n 个。

    输入格式
    第 1 行包含 1 个整数正 n(n≤400000)。 第 2 行与第 3 行分别有 n 个整数,各代表有序表 A 和 B。一行中的每两个整数之间用一个空格隔开,大小在长整型范围内,数据保证有序表单调递增。

    输出格式
    输出共 n 行,每行一个整数,第 i 行为第 i 小的和。 数据保证在 long long 范围内。

    样例输入

    3
    1 2 5
    2 4 7
    

    样例输出

    3
    4
    5
    

    分析

    可以枚举所有和,再压入小根堆优先队列,因为优先队列的性质,所以输出前n个队头元素即可。但因为n的范围是400000,所以这种n方的算法一定会TLE。。。

    由于两个已知数列的有序性,所以可得

    第一行    A[1]+B[1] ≤ A[1]+B[2] ≤ A[1]+B[3] ≤ ······
    第二行    A[2]+B[1] ≤ A[2]+B[2] ≤ A[2]+B[3] ≤ ······
    ······
    第n行     A[n]+B[1] ≤ A[n]+B[2] ≤ A[n]+B[3] ≤ ······
    

    那就先把每一行的第一个(即最小值)压入优先队列,取出队头元素(最小值)并输出,如果取的是第i行的元素,就把第i行的下一个元素压入,让堆中始终保持n个元素和。

    AC代码

    #include <cstdio>
    #include <queue>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 400005;
    long long a[MAXN], b[MAXN], c[MAXN];
    
    struct node {
    	int id, v; // id代表是第几行,v代表实际值
    	bool operator<(const node x) const {return v > x.v;} // 运算符重载
    };
    priority_queue <node> q; // 优先队列的定义
    
    int main() {
    	int n, m;
    	scanf ("%d", &n);
    	for(int i = 1; i <= n; i++) scanf ("%lld", &a[i]);
    	for(int i = 1; i <= n; i++) scanf ("%lld", &b[i]);	
    	for(int i = 1; i <= n; i++) {
    		c[i] = 2;
    		node t;
    		t.v = a[i] + b[1]; // 将每行第一个进入队列
    		t.id = i; // 存储是第几行的
    		q.push(t);
    	}
    	for(int i = 1; i <= n; i++) {
    		node t = q.top(); // 取出队头元素
    		q.pop(); 
    		printf("%d
    ", t.v);
    		t.v = a[t.id] + b[c[t.id]++]; // 计算出下一个
    		q.push(t); // 进入队列
    	}
    	return 0;
    }
    

    3.stack 栈

    头文件:

    #include <stack>
    using namespace std;
    

    简介:后进先出的无限长度数组。。。

    基本操作函数:

    • s.push():从栈顶入栈
    • s.pop():从栈顶出栈
    • s.top():栈顶元素
    • s.empty:判断是否为空

    题目:简单计算器

    题目描述
    读入一个只包含+、-、*、/、的非负整数计算表达式,计算该表达式的值。

    • 输入格式
      测试数据有多组,每组占一行。
      每行不超过200个字符,整数和运算符之间用一个空格分隔。
      没有非法表达式。

      当一行中只有一个0时,表示输入结束,相应的结果不要输出。

    • 输出格式
      对于每组测试数据输出一行,即该表达式的值,精确到小数点后两位。

    输入样例

    30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
    0
    

    输出样例

    12178.21
    

    分析
    当读入到一个符号的时候,从数字栈顶揪出一个数,进行计算,再重新压入栈呐~

    AC代码

    #include <cstdio>
    #include <stack>
    using namespace std;
    stack<double> s; // 栈的定义
    
    int main() {
    	double x;
    	char c;
    	while(scanf ("%lf%c", &x, &c)) {
    		if(x == 0 && c != ' ') return 0;
    		double m;
    		char a, b;
    		s.push(x);
    		while(scanf ("%c %lf%c", &a, &m, &b) != EOF) { // 输入
    			if(a == '+') s.push(m); // 如果是加号,压入正m
    			else if(a == '-') s.push(-m); // 是减号,压入负m
    			else if(a == '*' && !s.empty()) { // 如果是乘号,且数字栈不为空
    				double t = s.top(); // 取出一个
    				s.pop();
    				t *= m;
    				s.push(t); // 压入 t*m
    			}
    			else if(a == '/' && !s.empty()) { // 如果是除号,且数字栈不为空
    				double t = s.top(); // 取出一个
    				s.pop();
    				t /= m;
    				s.push(t); // 压入 t/m
    			}			
    			if(b != ' ') break;
    		}
    		double ans = 0;
    		while(!s.empty()) {
    			ans += s.top(); // 累计数字栈里的答案即可
    			s.pop();
    		}
    		printf("%.2lf
    ", ans);
    	}
    	return 0;
    }
    

    4.vector 动态数组

    头文件:

    #include <vector>
    using namespace std;
    

    简介:无限长度的数组。。。

    基本操作函数:

    • v.push_back():插入元素到数组的尾部
    • v.pop_back():删除数组尾部的元素
    • v.size():求数组长度
    • v.empty():判断数组是否为空
    • v.clear():把数组清空
    • 迭代器:迭代器就像STL的指针,返回一个地址,可以用*操作符得到其对应的具体的值
      声明方法:vector::iterator it
    • v.begin():返回指向数组第一个元素的迭代器
    • v.end():返回指向数组最后一个元素的迭代器
    • v.front():返回数组第一个元素
    • v.back():返回数组最后一个元素

    题目:上网统计

    题目描述
    在一个网络系统中有N个用户1≤N≤1000、M次上网记录1≤M≤5000。每个用户可以自己注册一个用户名,每个用户名是一个只含小写字母的字符串。每个上网的账号每次上网都会浏览网页,网页名是一串只含小写字母的字符串,每次上网日志都会留下记录,现在请你统计一次上网日志中,每个用户浏览了多少个网页。(输出按照输入顺序输出)

    • 输入格式
      第一行N和M 第2行到第M+1行为M条上网日志,每行两个字符串,用空格隔开

    • 输出格式
      N个ID的上网记录,具体看样例

    样例输入

    5 7
    guomao wangyi
    lifan tengxun
    zhoushijian souhu
    zhangshilin tengxun
    guomao souhu
    zhoushijian wangyi
    liuyang bilibili
    

    样例输出

    guomao wangyi souhu
    lifan tengxun
    zhoushijian souhu wangyi
    zhangshilin tengxun
    liuyang bilibili
    

    分析
    此题GM讲过的哦~
    看看各种函数及迭代器的使用。。。

    AC代码

    #include <cstdio> 
    #include <string> 
    #include <iostream> 
    #include <vector>
    using namespace std;
    
    const int MAXN = 1005;
    vector <string> v[MAXN]; // 动态数组的定义
    struct node{
    	int index;
    	string c;
    } s[MAXN];
    
    int main() {
    	int n, m, t = 0;
    	scanf ("%d %d", &n, &m);
    	for(int i = 1; i <= m; i++) {
    		string ch, k;
    		getchar();
    		cin >> ch;
    		cin >> k;
    		int index_ = 0;
    		for(int j = 1; j <= t; j++) {
    			if(ch == s[j].c) 
    				index_ = s[j].index;
    		}
    		if(index_ == 0) {
    			t++;
    			index_ = t;
    			s[index_].index = t;
    			s[index_].c = ch;			
    		}
    		v[index_].push_back(k); // 在最后插入一个元素
    	} 
    	for(int i = 1; i <= n; i++) {
    		cout << s[i].c << " ";
    		for(vector<string>::iterator it = v[s[i].index].begin(); it != v[s[i].index].end(); it++) { 
    		// 利用迭代器进行输出,将其初值置为指向第一个元素的迭代器,如果当前迭代器没有指向最后一个元素,迭代器加加
    			cout << *it << " "; // 输出当前迭代器对应的具体的值
    		}
    		cout << endl;
    	}
    	return 0;
    }
    

    5.map

    头文件:

    #include <map>
    using namespace std;
    

    简介:映射,可以看作数组下标为任意类型的数组

    基本操作函数:

    • v.empty():判断数组是否为空
    • v.clear():把数组清空
    • 迭代器声明方法:map<int, int>::iterator it
      且 it 对应的具体的值是一个pair
    • v.begin():返回指向数组第一个元素的迭代器
    • v.end():返回指向数组最后一个元素的迭代器
    • v.insert():插入一个元素

    题目:T1 查字典

    题目描述
    gm英语非常不好,为了应对全国英文四级考试,他手里有一本英语字典,现在有很多单词要查。请编写程序帮助他快速找到要查的单词所在的页码。

    • 输入格式
      第一行1个整数N,N≤10000,表示字典中一共有多少单词。

      接下来每两行1个单词,其中:第一行是长度≤100的字符串,表示这个单词,全是小写字母,单词不会重复。 第二行是1个整数,表示这个单词在字典中的页码。

      接下来是一个整数M,M≤N,表示要查的单词数。 接下来M行,每行一个字符串,表示要查的单词,保证在字典中存在。

    • 输出格式
      M行,每行1个整数,表示第i个单词在字典中的页码。

    样例输入

    2
    scan
    10
    word
    15
    2
    scan
    word
    

    样例输出

    10
    15
    

    分析
    是裸题诶……以单词为下标保存页码的数组就可以了

    AC代码

    #include <cstdio>
    #include <map>
    #include <string>
    #include <iostream>
    using namespace std;
    
    map<string, int> mp; // 映射的定义
    
    int main() {
    	int n;
    	scanf ("%d", &n);
    	for(int i = 1; i <= n; i++) {
    		string s;
    		cin >> s;
    		int x;
    		cin >> x;
    		mp[s] = x;
    	// 可以像数组一样赋值
    	// 此题也可以利用 pair 写为 mp.insert(make_pair(s,x));
    	}
    	int m;
    	scanf ("%d", &m);
    	for(int i = 1; i <= m; i++) {
    		string s;
    		cin >> s;
    		printf("%d
    ", mp[s]);
    	} 
    	return 0;
    }
    
    T2 Let the Balloon Rise

    小气球~~气球~

    题目描述
    在ACM比赛中,你每解决一道题,你就可以获得一个气球,不同颜色的气球代表你解决了不同的问题。在GM同学参加的一场ACM比赛中,他发现场面上有N个气球,并熟练的说出了气球的颜色。

    请你编写一个程序,找出气球数量最多的颜色。

    • 输入格式
      有多组样例输入。

      每组样例第一行输入一个整数N (0 < N <= 1000) ,代表一共有N个气球。若N=0,则代表输入结束。

      接下来N行每行输入一个不多于15个字母的字符串代表颜色。

    • 输出格式
      对于每组样例数据,在单独的一行内输出数量最多的那种颜色的气球。(数据保证输出是唯一的)

    样例输入

    5
    green
    red
    blue
    red
    red
    3
    pink
    orange
    pink
    0
    

    样例输出

    red
    pink
    

    分析
    还是裸a!~!!
    以颜色为下标存储出现次数的数组

    #include <cstdio>
    #include <map>
    #include <string>
    #include <iostream>
    using namespace std;
    
    const int MAXN = 10005;
    string s[MAXN];
    
    int main() {
    	int n;
    	while(scanf ("%d", &n) != EOF) {
    		if(n == 0) break;
    		map<string, int> mp; // map 的定义
    		for(int i = 1; i <= n; i++) {
    			cin >> s[i];
    			mp[s[i]]++; // 颜色 s[i] 出现的次数增加
    		}
    		string ch;
    		int ma = 0;
    		for(int i = 1; i <= n; i++) {
    			if(mp[s[i]] >= ma) {
    				ma = mp[s[i]];
    				ch = s[i];
    			}
    		}
    		cout << ch << endl;
    	}
    	return 0;
    }
    

    6.set 有序集合

    头文件:

    #include <set>
    using namespace std;
    

    简介:set 有序的无重复的集合,multiset 有序的可重复的集合

    基本操作函数:(set 和 multiset 相同)

    • s.size():求集合长度
    • s.empty():判断集合是否为空
    • s.clear():把集合清空
    • 迭代器声明方法:set::iterator it
    • s.begin():返回指向集合第一个元素的迭代器
    • s.end():返回指向集合最后一个元素的迭代器
    • s.insert():插入一个元素
    • s.count():返回集合中等于某个数的元素个数

    特别注意:set 和 multiset 也需要重载运算符


    题目:【STL综合】题海战

    题目描述
    某信息学奥赛教练经验丰富,他的内部题库有 m 道题。他有 n 个学生,第 i 个学生已经做过p[i]道题。由于马上要进行noip考试,该教练准备举行 k 场比赛和训练。每场比赛或训练都会有一些他的学生参加,但是如何选题令他非常烦恼。对于每场比赛,他要保证所出的题没有任何一道已有任何一个学生做过;而对于每场训练,他要保证所出的所有题都被每一个参赛学生做过。

    • 输入格式
      第1行2个正整数n和m,表示学生数和题库中的题目总量。

      第2~n+1行,先是1个正整数p,然后p个整数表示第i个学生的做题记录(可以重复做同一道题)。

      第n+2行,1个正整数k,表示要举行比赛和训练的总场数(可能有学生重复报名)。

      接下来的k行,每行的第1个整数type表示是训练或者比赛(1为训练,0为比赛)。第二个数q表示参赛学生数,然后q个正整数表示参赛学生编号。每一行的两个数之间有一个空格。

    • 输出格式
      共k行,每行表示本次训练或比赛可选的最多题目(由小到大排序,中间用一个空格隔开,如果没有输出一个空行)。

    样例输入

    5 10
    2 3 7
    1 3
    2 4 7
    3 3 6 10
    7 1 2 3 4 7 8 9
    6
    0 3 3 4 5
    0 3 1 3 4
    1 2 1 3
    0 1 5
    1 1 2
    1 2 3 5
    

    样例输出

    5
    1 2 5 8 9
    7
    5 6 10
    3
    4 7
    

    分析
    利用动态数组存储每一个学生做过的题目
    在输出时利用映射求出每个参加训练的学生都做过的题
    利用有序集合求出每个参加比赛的学生都没做过的题

    #include <cstdio>
    #include <set>
    #include <map>
    #include <vector>
    using namespace std;
    
    const int MAXN = 10005;
    vector<int> v[MAXN]; // 动态数组的定义
    set<int> s; // 有序集合的定义
    map<int, int> mp; // 映射的定义
    
    int main() {
    	int n, m;
    	scanf ("%d %d", &n, &m);
    	for(int i = 1; i <= n; i++) {
    		int n_;
    		scanf ("%d", &n_);
    		for(int j = 1; j <= n_; j++) {
    			int x;
    			scanf ("%d", &x);
    			v[i].push_back(x);
    		}
    	}
    	int k;
    	scanf ("%d", &k);
    	for(int i = 1; i <= k; i++) {
    		int flag;
    		s.clear();
    		mp.clear();
    		scanf ("%d", &flag);
    		if(flag == 0) {
    			int n_;
    			scanf ("%d", &n_);
    			for(int j = 1; j <= n_; j++) {
    				int stu;
    				scanf ("%d", &stu);
    				for(int k_ = 0; k_ < v[stu].size(); k_++) // 统计每道题被几个学生做过
    					mp[v[stu][k_]]++;
    			}	
    			for(int j = 1; j <= m; j++) { 
    				if(mp[j] == n_) // 如果都做过就输出
    					printf("%d ", j);
    			}					
    			printf("
    ");
    		}
    		else {
    			
    			int n_;
    			scanf ("%d", &n_);
    			for(int j = 1; j <= n_; j++) {
    				int stu;
    				scanf ("%d", &stu);
    				for(int k_ = 0; k_ < v[stu].size(); k_++)
    					s.insert(v[stu][k_]); // 加入有序集合
    			}	
    			bool flag[MAXN] = {0};
    			for(set<int>::iterator it = s.begin(); it != s.end(); it++) // 迭代器遍历,把所有有学生做过的题目标记
    				flag[*it] = true;
    			for(int j = 1; j <= m; j++) {
    				if(flag[j] == false) // 未被标记的即可输出
    					printf("%d ", j); 
    			}			
    			printf("
    ");			
    		}
    	} 
    	return 0;
    }
    

    突破极限,一旦放弃了就意味着结束,说不定身体里还隐藏着连自己都没有察觉到的力量,不要被所谓的极限所禁锢。

  • 相关阅读:
    Swift 设置navigation左右两侧按钮
    Tab Bar Controller和Navigation Controller混合使用详细教程
    导航栏控制器和标签栏控制器(UINavigationController和UITabBarController)混用
    UIViewController、UINavigationController与UITabBarController的整合使用
    iOS开发UI篇—UIWindow简单介绍
    mod_wsgi + pymssql通路SQL Server座
    UVA 11464 Even Parity(递归枚举)
    iOS kvc
    数据的同步为每个站点创建触发器同步表
    精彩编码 【进制转换】
  • 原文地址:https://www.cnblogs.com/Chain-Forward-Star/p/13868133.html
Copyright © 2011-2022 走看看