[关闭]
@Moritz 2019-03-26T04:27:12.000000Z 字数 6398 阅读 422

第五章 C++与STL入门

aoapc_2nd C++ 所有文稿


5.1 从C到C++

5.1.1 C++版框架


5.2 STL初步

5.2.1 排序与检索

例题 5-1 大理石在哪儿

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <stdlib.h>
  4. using namespace std;
  5. const int MAXN=10000;
  6. int main()
  7. {
  8. int N,Q,x,kase=0,a[MAXN];
  9. /*scanf()的返回值是成功赋值的变量数量, 发生错误时返回EOF,变量名前加取地址符&*/
  10. while(scanf("%d%d",&N,&Q)==2&&N){//N=0时停止输入
  11. printf("CASE# %d:\n",++kase);
  12. for(int i=0;i<N;i++) scanf("%d",&a[i]);
  13. sort(a,a+N);//STL排序,参数为指针/迭代器
  14. while(Q--)
  15. {
  16. scanf("%d",&x);
  17. int p=lower_bound(a,a+N,x)-a;//-a后得到和a地址(下标)相差数
  18. if(a[p]==x) printf("%d found at %d\n",x,p+1);//题目中数组序号为1~N,所以p+1
  19. else printf("Not found.\n");
  20. }
  21. }
  22. return 0;
  23. }

5.2.2 不定长数组vector

例题5-2太烦了,写了一半,未完成。 -2019.2.27

5.2.3 集合set

例题 5-3 安迪的第一个字典

  1. #include <iostream>
  2. #include <set>
  3. #include <string.h>
  4. #include <sstream>
  5. using namespace std;
  6. int main()
  7. {
  8. string s,buf;
  9. set<string> dict;
  10. while(cin>>s)
  11. {
  12. if (s=="00") break;
  13. for(int i=0;i<s.length();i++)
  14. {
  15. if (isalpha(s[i])) s[i]=tolower(s[i]);//把大写转化成小写
  16. else s[i]=' ';//非字母的变为空格,字符串流中再读入则会拆分成两个单词
  17. }
  18. cout<<s<<endl;
  19. stringstream ss(s);
  20. while(ss>>buf) dict.insert(buf);
  21. }
  22. /*注意迭代器不能比较,终止条件只能写成不等于尾后迭代器*/
  23. for(auto it=dict.begin();it!=dict.end();it++)
  24. cout<<*it<<endl;
  25. return 0;
  26. }

5.2.4 映射:map

map就是从键key到值value的映射。因为重载了[]运算符,map像是数组的”高级版“。例如可以用一个map<string,int> month_name来表示“月份名字到月份编号”的映射,然后用month_name["July"]=7这样的方式来赋值。

例题 5-3 反片语

  1. #include <iostream>
  2. #include <map>
  3. #include <string>
  4. #include <set>
  5. #include <algorithm>
  6. #include <vector>
  7. using namespace std;
  8. /*用函数实现标准化功能并单向传递值:便于标准化后遍历vector(未标准化的)元素查看其标准化形式(不改变元素的前提下)是否数量多于一,如果不是,则插入set中输出(未标准化的形式)*/
  9. string sta(string s)
  10. {
  11. for(int i=0;i<s.length();i++)
  12. {
  13. if (isalpha(s[i])) s[i]=tolower(s[i]);
  14. }
  15. sort(s.begin(),s.end());//必须用迭代器,不能用s[0]和s[s.length()](Why?)
  16. return s;
  17. }
  18. int main()
  19. {
  20. string s;
  21. vector<string> v;
  22. set<string> se;
  23. map<string,int> m;
  24. while(cin>>s)
  25. {
  26. if (s[0]=='#') break;
  27. v.push_back(s);
  28. s=sta(s);
  29. if (!m.count(s)) m[s]=0;//返回map中键值等于key的元素的个数,键值为标准化字符串,值为个数
  30. m[s]++;
  31. //cout<<s<<" "<<m[s]<<endl;
  32. }
  33. for(int i=0;i<v.size();i++)
  34. {
  35. if (m[sta(v[i])]==1) se.insert(v[i]);
  36. }
  37. for(auto it=se.begin();it!=se.end();it++) cout<<*it<<endl;
  38. return 0;
  39. }

5.2.5 栈、队列与优先队列

例题 5-5 集合栈计算机

暂未完成。 -2019.2.27

例题 5-6 团队队列

  1. #include <iostream>
  2. #include <queue>
  3. #include <map>
  4. #include <stdio.h>
  5. using namespace std;
  6. const int maxt=1010;
  7. int main()
  8. {
  9. int t,number;
  10. /*记录团队编号*/
  11. map<int,int> team;
  12. scanf("%d",&t);
  13. for(int i=1;i<=t;i++)//第i个团队的成员
  14. {
  15. int n,x;
  16. scanf("%d",&n);//第i个团队有t人
  17. while(n--)
  18. {
  19. scanf("%d",&x);
  20. team[x]=i;
  21. }
  22. }
  23. /*模拟命令*/
  24. queue<int> q,q2[maxt];//q为团队队列,q2[i]为团队i内成员队列
  25. while(1)
  26. {
  27. char cmd[10];
  28. scanf("%s",cmd);//cmd作为数组首元素已经是地址,不用加&
  29. if (cmd[0]=='S') break;
  30. else if (cmd[0]=='E')
  31. {
  32. int x;
  33. scanf("%d",&x);
  34. if(q2[team[x]].empty()) q.push(team[x]);//team[x]为队员x所在的团队名
  35. q2[team[x]].push(x);
  36. }
  37. else if (cmd[0]=='D')
  38. {
  39. cout<<q2[q.front()].front()<<endl;//q.front()为团队编号
  40. q2[q.front()].pop();
  41. if (q2[q.front()].empty()) q.pop();
  42. }
  43. }
  44. return 0;
  45. }

队列中,用q.push(x)q.pop()来进行元素的入队和出队操作,q.front()q.back()取首尾元素,以及q.size()q.empty()函数。

STL的优先队列也定义在<queue>中,用priority_queue<int> pq来声明,越小的整数优先级越低,只要元素定义了“小于”运算符,就可以使用优先队列。返回优先级最高的元素方法由queuefront()变为top()。越小的整数优先级越大的优先队列可以写成priority_queue<int,vector<int>,greater<int> > qq

使用自定义方式比较优先级,详见书本P119

生成随机数

核心函数是cstdlib中的rand(),需要随机数的程序在最开始执行一次srand(time(MULL))来初始化随机数种子。

vectorsetmap都很快(*注意vector并不是所有操作都很快,push_front()操作会引起所有元素往后移动,实际上是很慢的),其中vector的速度接近数组,setmap的速度远远超过”用一个vector保存所有值,然后逐个元素进行查找“时的速度。


5.3 应用:大整数类

5.3.1 大整数类BigInteger

5.3.2 四则运算

5.3.3 比较运算符


5.4 竞赛题目举例


5.5 习题

5-1 代码对齐


5-2 Ducci序列

根据题目条件“输入保证最多1000步就会变成0或者循环”,思路则可以确定为循环1000次,若不为0,则判断为循环

  1. #include <iostream>
  2. #include <vector>
  3. #include <stdlib.h>
  4. using namespace std;
  5. int main()
  6. {
  7. int n,x,t=1010;
  8. bool turn_0=true;
  9. cin>>n;
  10. vector<int> v1,v2{0,0,0,0};//注意,vector的下标访问不可以创建元素!!
  11. for(int i=0;i<n;i++)
  12. {
  13. cin>>x;
  14. v1.push_back(x);
  15. }
  16. while(t--)
  17. {
  18. turn_0=true;
  19. for(int i=0;i<n-1;i++)
  20. {
  21. v2[i]=abs(v1[i]-v1[i+1]);//注意,vector的下标访问不可以创建元素!!
  22. if(v2[i]!=0) turn_0=false;
  23. }
  24. v2[n-1]=abs(v1[n-1]-v1[0]);
  25. if(v2[n-1]!=0) turn_0=false;
  26. if (turn_0) {cout<<"ZERO"<<endl;break;}
  27. v1=v2;
  28. }
  29. if (!turn_0) cout<<"LOOP"<<endl;
  30. return 0;
  31. }

《习题选解》书上采用set判断重复,重写后代码如下

  1. vector<int> du,zero;
  2. set<vector<int>> dus;
  3. int n,x;cin>>n;
  4. for(int i=0;i<n;i++)
  5. {
  6. cin>>x;
  7. du.push_back(x);
  8. zero.push_back(0);
  9. }
  10. dus.insert(du);
  11. for(int i=0;i<1010;i++)
  12. {
  13. int t=du[0];//注意用临时变量存储在循环中被改变的du[0]
  14. for(int j=0;j<n-1;j++) du[j]=abs(du[j]-du[j+1]);
  15. du[n-1]=abs(du[n-1]-t);
  16. if (du==zero) break;
  17. else if (dus.count(du)) break;
  18. else dus.insert(du);
  19. }
  20. if (du==zero) cout<<"ZERO"<<endl;
  21. else cout<<"LOOP"<<endl;

主要利用vector重载等号运算符==可以直接进行比较,setcount()函数查找重复序列。


5-3 卡片游戏

比较简单,n数值不大,思考有没有更简便的方法可以根据输入n直接算出最后结果。


5-4 交换学生

思路是用map以一个包含两个元素的数组为键值,如果找得到元素位置对调的数组键值,则代表匹配成功。

  1. map<int[2],int> can;
  2. int n;
  3. while (scanf("%d",&n)==1&&n)
  4. {
  5. can.clear();
  6. int m=0;
  7. while (n--)
  8. {
  9. int a[2];
  10. cin>>a[0]>>a[1];
  11. int b[2]={a[1],a[0]};
  12. if (can.count(b))
  13. {
  14. can[a]=1;
  15. can[b]=1;
  16. m--;
  17. }
  18. else
  19. {
  20. can[a]=0;
  21. m++;
  22. }
  23. }
  24. bool com=true;
  25. for(auto &i:can)
  26. if (i.second==0)
  27. {com=false;break; }
  28. if (com) cout<<"YES"<<endl;
  29. else cout<<"NO"<<endl
  30. }

但报错为array used as initializer,不懂,未解决。 -2019.3.1

然后(学会了pair之后)改为运用pair版本

  1. map<pair<int,int>,int> can;
  2. int n;
  3. while (scanf("%d",&n)==1&&n)
  4. {
  5. can.clear();
  6. int m=0;
  7. while (n--)
  8. {
  9. int a,b;
  10. cin>>a>>b;
  11. if (can.count(make_pair(b,a)))//表示pair的方法,目前只会make_pair
  12. {
  13. can[make_pair(b,a)]=1;
  14. can[make_pair(a,b)]=1;
  15. m--;
  16. }
  17. else
  18. {
  19. can[make_pair(a,b)]=0;
  20. m++;
  21. }
  22. }
  23. bool com=true;
  24. for(auto &i:can)
  25. {
  26. if (i.second==0)
  27. {
  28. com=false;
  29. break;
  30. }
  31. }
  32. if (com) cout<<"YES"<<endl;
  33. else cout<<"NO"<<endl;
  34. }

5-5 复合词

大脑进入停机预备阶段

思路:如果是复合词的话,排序必然在该单词前半部分对应的单词的后方并且字符串长度更长,在set中查找后半部分的单词即可。

  1. /*未施工完毕的代码*/
  2. #include <iostream>
  3. #include <set>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <iterator>
  7. using namespace std;
  8. int main()
  9. {
  10. int len=0;
  11. string s;
  12. set<string> dict;
  13. while(cin>>s) dict.insert(s);
  14. len=(*(dict.begin())).length();
  15. auto it=dict.begin();
  16. for(auto i:dict)
  17. {
  18. if ((i).length()>len)
  19. {
  20. string st;
  21. for(int c=len;c<(i).length();c++) st+=(i)[c];
  22. }
  23. else
  24. {
  25. len=(i).length();
  26. it=i;//错误,迭代器没有=运算符,WHY?
  27. }
  28. }
  29. return 0;
  30. }

以下为习题选解上的答案

  1. set<string> words;
  2. string word,left,right;
  3. while(cin>>word) words.insert(word);
  4. for(const auto& s: words)
  5. {
  6. for(int j=1;j<s.size();j++)
  7. {
  8. left.assign(s,0,j);//string类的assign函数(没想到吧!
  9. if (words.count(left))
  10. {
  11. right.assign(s,j,s.size()-j);
  12. if (words.count(right)) {cout<<s<<endl;break;}
  13. }
  14. }
  15. }

思路主要卡壳与不知道怎么拆分字符串,其实一个简单的assign函数即可。


5-6 对称轴

数学题,如果存在对称轴x=m,那么m一定是所有点x坐标的平均值

其实看漏题目了,题目要求找的对称轴是竖线:)


2019.3.2

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注