算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题(9/12)
来源:互联网 发布:神奇百货ceo骗局 知乎 编辑:程序博客网 时间:2024/04/30 00:27
说明
本文是我对第五章12道例题的练习总结,建议配合紫书——《算法竞赛入门经典(第2版)》阅读本文。
另外为了方便做题,我在VOJ上开了一个contest,欢迎一起在上面做:第五章例题contest
如果想直接看某道题,请点开目录后点开相应的题目!!!
例题
例5-1 UVA 10474 大理石在哪儿 (排序和查找)
思路
这个题比较基础,排序用sort,查找用lower_bound,都是STL的标准函数。注意lower_bound函数的返回值是不小于查找数的第一个位置对应的指针,找不到则返回a+n(也就是函数的第二个参数)。
代码
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;const int N = 10000;int main(void){ int n, q, t = 0; int a[N], x; while (scanf("%d%d", &n, &q) != EOF && n) { for (int i = 0; i < n; i ++) scanf("%d", &a[i]); sort(a, a+n); printf("CASE# %d:\n", ++t); while (q--) { scanf("%d", &x); int m = lower_bound(a, a+n, x) - a; printf("%d ", x); if (m == n || a[m] != x) printf("not found\n"); else printf("found at %d\n", m+1); } } return 0;}
例5-2 UVA 101 木块问题 (using std::vector)
思路
由于此题中木块堆的长度一直在变化,并需要频繁的添加和删除元素操作,用vector是最合适的。
注意学习vector的功能函数用法,如size() resize() push_back()等。
另外,这个题的四种操作中具体操作有一定的重复,合理拆分操作能够使代码模块化更好。
代码
#include <iostream>#include <cstdio>#include <algorithm>#include <vector>using namespace std;const int N = 25;typedef pair<int, int> P;int n;vector<int> pile[N];P find_pile(int a){ for (int i = 0; i < n; i ++) { for (int j = 0; j < pile[i].size(); j ++) { if (pile[i][j] == a) return P(i, j); } } return P(n, n);}void clear_above(P p){ int i = p.first, j = p.second; for (int k = j+1; k < pile[i].size(); k ++) { int a = pile[i][k]; pile[a].push_back(a); } pile[i].resize(j+1);}void pile_over(P p, int b){ int i = p.first, j = p.second; for (int k = j; k < pile[i].size(); k ++) { int a = pile[i][k]; pile[b].push_back(a); } pile[i].resize(j);}void print(){ for (int i = 0; i < n; i ++) { printf("%d:", i); for (int j = 0; j < pile[i].size(); j ++) { printf(" %d", pile[i][j]); } printf("\n"); }}int main(void){ cin >> n; for (int i = 0; i < n; i ++) pile[i].push_back(i); string s1, s2; int a, b; P p1, p2; while (cin >> s1 && s1 != "quit") { cin >> a >> s2 >> b; p1 = find_pile(a); p2 = find_pile(b); if (p1.first == p2.first) continue; if (s1 == "move") clear_above(p1); if (s2 == "onto") clear_above(p2); pile_over(p1, p2.first); //print(); //printf("===============\n"); } print(); return 0;}
例5-3 UVA 10815 安迪的第一本字典 (using std::set)
思路
此题中涉及的知识点有:
1、set的用法(注意set中没有重复的值)
2、isalpha isdigit等函数的用法
3、迭代器iterator的用法
注意学习。
代码
#include <cstdio>#include <iostream>#include <string>#include <cctype>#include <set>using namespace std;set<string> d;int main() { string s; char ch; while (getline(cin, s)) { for (int i = 0; i < s.size(); i++) { if (!isalpha(s[i])) continue; string tmp; while (i < s.size() && isalpha(s[i])) tmp += tolower(s[i++]); d.insert(tmp); } } for (set<string>::iterator i = d.begin(); i != d.end(); i++) cout << *i << endl; return 0; }
例5-4 UVA 156 反片语 (using std::map)
思路
我的代码用了两个map,不如书中的例程代码用了一个map和一个vector。
代码
#include <iostream>#include <algorithm>#include <string>#include <map>#include <set>using namespace std;map<string, string> sorted;map<string, int> num;int main(void){ string s, s1; char ch[21]; while (cin >> s && s != "#") { int n = s.size(); for (int i = 0; i < s.size(); i ++) ch[i] = tolower(s[i]); ch[n] = '\0'; sort(ch, ch+n); s1 = ch; sorted[s] = s1; if (num.find(s1) == num.end()) num[s1] = 1; else num[s1] ++; } set<string> res; for (map<string, string>::iterator it = sorted.begin(); it != sorted.end(); it ++) { if (num[it->second] == 1) res.insert(it->first); } for (set<string>::iterator it = res.begin(); it != res.end(); it ++) { cout << *it << endl; } return 0;}
例5-5 UVA 12096 集合栈计算机 (using std::stack and other containers)
思路
这个题主要使用的数据结构是stack,但最重要的知识点却不是stack!
本题有一种用映射简化数据结构的思想:将另一种相对复杂的数据结构用map映射成int型id,不仅能够大大降低存储空间,而且也可以大大提高查找效率。
因此这是第五章最重要的一道题,没有之一!!!
后续习题或例题中有好几道用到了这种思想,后面会讲到。
代码
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<set>#include<map>#include<vector>#include<stack>using namespace std;typedef set<int> Set;map<Set, int> IDs;vector<Set> Sets;stack<int> st;int getID(Set s){ if (IDs.count(s)) return IDs[s]; Sets.push_back(s); return IDs[s] = Sets.size()-1;}void init(){ Set s0; getID(s0);}void push(){ st.push(0);}int main(){ int t, n; scanf("%d", &t); while (t--) { scanf("%d", &n); init(); char op[10]; Set s1, s2, s3; int id; while (n--) { scanf("%s", op); switch(op[0]) { case 'P': st.push(0); break; case 'D': st.push(st.top()); break; case 'U': s1 = Sets[st.top()]; st.pop(); s2 = Sets[st.top()]; st.pop(); for (Set::iterator it = s2.begin(); it != s2.end(); it++) s1.insert(*it); st.push(getID(s1)); break; case 'I': s1 = Sets[st.top()]; st.pop(); s2 = Sets[st.top()]; st.pop(); s3.clear(); for (Set::iterator it = s2.begin(); it != s2.end(); it++) if (s1.count(*it)) s3.insert(*it); st.push(getID(s3)); break; case 'A': id = st.top(); st.pop(); s2 = Sets[st.top()]; st.pop(); s2.insert(id); st.push(getID(s2)); break; default: break; } printf("%d\n", Sets[st.top()].size()); } printf("***\n"); } return 0;}
例5-6 UVA 540 团体队列 (using std::queue and other containers)
思路
队列的特点是先入先出,通常排队系统的题会用到队列。
本题用两个队列解决:每个团体有一个队列(元素为团体成员ID),团队整体又形成一个队列(元素为团体ID)。
代码
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <map>#include <queue>using namespace std;const int T = 1000;int t;map<int, int> his_team;queue<int> qt, q[T];void init_queue(){ while (qt.size()) qt.pop(); for (int i = 0; i < t; i++) while (q[i].size()) q[i].pop();}int main(void){ int kase = 0, n, m; char op[10]; while (scanf("%d", &t) && t) { for (int i = 0; i < t; i++) { scanf("%d", &n); for (int j = 0; j < n; j++) { scanf("%d", &m); his_team[m] = i; } } init_queue(); printf("Scenario #%d\n", ++kase); while (scanf("%s", op) && strcmp(op, "STOP")) { if (op[0] == 'E') { scanf("%d", &m); int i = his_team[m]; if (q[i].empty()) qt.push(i); q[i].push(m); } else { int i = qt.front(); printf("%d\n", q[i].front()); q[i].pop(); if (q[i].empty()) qt.pop(); } } printf("\n"); } return 0;}
例5-7 UVA 136 丑数 (using priority_queue)
思路
这个题的uva上一直submit error,但我程序的运行结果与其他博客对比过,完全正确。
此题没有必要一定用long long,因为所求结果(第1500个丑数)并不超过int表示范围。但我结果*5却会超过,所以我的程序中用了long long。
代码
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<queue>using namespace std;typedef long long LL;int main(){ LL a = 0, b = 0; priority_queue<LL, vector<LL>, greater<LL> > pq; pq.push(1); int cnt = 0; while (pq.size()) { a = pq.top(); pq.pop(); if (a != b) { cnt++; if (cnt == 1500) break; pq.push(a*2); pq.push(a*3); pq.push(a*5); b = a; } } printf("%lld\n", a); return 0;}
例5-8 UVA 400 Unix is 命令 (排序和字符串处理)
思路
注意这个题要求的是按列优先方式输出,这与程序正常输出的方向不同。因此需要改变循环层次,具体见程序。
另外只要有字符后面就要补全M或M+2个字符,如果没有字符则不用。
代码
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const int N = 100;const int M = 60;void print_char(int k, char c){ while (k--) printf("%c", c);}int main(){ int n, m, col, row; string s[N]; while (~scanf("%d", &n)) { m = 0; for (int i = 0; i < n; i++) { cin >> s[i]; m = max(m, (int)s[i].size()); } sort(s, s+n); col = (M-m)/(m+2) + 1; row = n/col + ((n%col) ? 1 : 0); print_char(M, '-'); printf("\n"); for (int j = 0; j < row; j ++) { for (int i = 0; i < col; i ++) { int k = i*row+j; if (i == col-1) { if (k < n) { cout << s[k]; print_char(m-s[k].size(), ' '); } printf("\n"); } else { cout << s[k]; print_char(m+2-s[k].size(), ' '); } } } } return 0;}
例5-9 UVA 1592 数据库 (uniquely using std::map)
思路
四重循环枚举肯定会超时,实际上存在大量无用的比较。实际上只需要枚举c1和c2,然后从上到下扫描各行。每次碰到一个新的行r,把c1,c2两列的内容作为一个二元组存到一个map中。如果map的键值中已经存在这个二元组,该二元组映射到的就是所要求的r1,而当前行就是r2。
还有一个细节问题:如何表示由c1,c2两列组成的二元组?一种方法是直接用两个字符串拼成一个长字符串,速度更快的则是进行预处理——给所有字符串分配一个编号,则整个数据库中每个单元格都变成了整数,上述二元组就变成了两个整数。这是例子5-5中介绍的技巧。
另外在做的时候有一个小插曲,第一次提交TLE了,查了一下程序发现每组数据计算时忘记重新对map进行初始化。
代码
#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<map>#include<vector>using namespace std;const int N = 10000;const int M = 10;typedef pair<int, int> P;map<P, int> firstpos;map<string, int> str2key;vector<string> key2str;int ID(string &s){ if (str2key.count(s)) return str2key[s]; key2str.push_back(s); return str2key[s] = key2str.size()-1;}int main(){ int r, c; char ch; string tab[N][M]; while (cin >> r >> c) { getchar(); for (int i = 0; i < r; i++) { for (int j = 0; j < c; j++) { tab[i][j] = ""; while ((ch = getchar()) != ',' && ch != '\n') tab[i][j] += ch; } } int flag = true; for (int j = 0; j < c; j++) { for (int k = j+1; k < c; k++) { firstpos.clear(); str2key.clear(); key2str.clear(); for (int i = 0; i < r; i++) { P p(ID(tab[i][j]), ID(tab[i][k])); if (firstpos.count(p)) {//find printf("NO\n"); printf("%d %d\n", firstpos[p]+1, i+1); printf("%d %d\n", j+1, k+1); flag = false; break; } else firstpos[p] = i; } if (flag == false) break; } if (flag == false) break; } if (flag) printf("YES\n"); } return 0;}
例5-10 UVA 207 PGA 巡回赛的奖金 (排序和其他细节处理)
思路
该题尚未尝试,先占个位置。
代码
例5-11 UVA 814 邮件传输代理的交互 (字符串以及 STL 容器综合运用)
思路
该题尚未尝试,先占个位置。
代码
例5-12 UVA 221 城市正视图 (离散化)
思路
该题尚未尝试,先占个位置。
代码
- 算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题(9/12)
- 算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 习题(13/16)
- 算法竞赛入门经典(第二版) 刘汝佳-第八章 高效算法设计 例题(13/19)
- 算法竞赛入门经典(第二版)-刘汝佳-第三章 数组与字符串 例题+习题(17/18)
- 算法竞赛入门经典(第二版)-刘汝佳-第四章 函数与递归 例题+习题(15/16)
- 算法竞赛入门经典(第二版)-刘汝佳-第六章 数据结构基础 例题(17/22)
- 算法竞赛入门经典(第二版)-刘汝佳-第七章 暴力求解法 例题(6/15)
- 算法竞赛入门经典(第二版)-刘汝佳-第九章 动态规划初步 例题(11/31)
- 算法竞赛入门经典(第二版)-刘汝佳-第三章-例题3-5生成元
- 算法竞赛入门经典(第二版)-刘汝佳-第三章-例题3-5 环状序列
- 算法竞赛入门经典(第二版)-刘汝佳-第五章 交流生
- 算法入门竞赛 第五章例题 题解
- ☆算法竞赛入门经典(第二版) 例题4-2 猜单词(Hangman Judge) UVa489
- 算法竞赛入门经典(第二版)紫书-题目集合【例题+习题】
- 算法竞赛入门经典(第二章)
- 算法竞赛入门经典 例题9-1
- 算法竞赛入门经典 例题 9-4
- 算法竞赛入门经典(第二版)-刘汝佳-第六章 数据结构基础 习题(12/14)
- c语言入门之项目2.5——求1到m的倒数的和
- java动态代理--一个简单的例子
- uva 11722 通过面积比算概率
- Sublime Text非官方文档(编辑)
- 给java初学者的建议
- 算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题(9/12)
- 机器学习题目汇总三
- poj 1860 Currency Exchange(最长路)
- C#对List同时遍历删除操作
- uva 10670 The jackpot
- android5.0之往toolbar上添加按钮
- MVP模式在Android开发中的最佳实践
- iphone,ipad,android图片尺寸
- 46条for_each循环优于传统for循环