提交地址: 算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题

中文题目地址:算法竞赛入门经典(第二版)-刘汝佳-第五章 C++与STL 例题与习题

5.1 从C到C++

5.2 STL初步

5.2.1 排序与检索

例题5-1:大理石在哪儿?

思路:

水题一道,直接暴力排序+检索即可。

代码:
#include <iostream>
#include <algorithm>using namespace std;const int N = 10010;int a[N];//石头数组
int main()
{int n, q, kase = 0;//每次读入一个n,q。直到n,q都为0为止。 while(cin >> n >> q, n || q){   //对于每一组测试数据都要输出 printf("CASE# %d:\n", ++kase);for(int i = 0; i < n; i ++) cin >> a[i];//读入每一个石头上的整数sort(a, a + n);//升序while(q--)//处理q个询问 {int x;cin >> x;int idx = lower_bound(a, a + n, x) - a;//在已经排好序的数组a中寻找x;if(a[idx] == x) printf("%d found at %d\n", x, idx + 1);else printf("%d not found\n", x);}}return 0;
}

总结:

  • 下面三种函数的作用区间都是左闭右开。
  • sort()函数:sort(start,end,cmp)。默认升序,start是要排序数组的起始地址,end是数组结束地址的下一位,cmp用于规定排序的方法,可不填,默认升序。
  • lower_bound()函数:lower_bound(begin,end,num)的含义是从数组的begin位置到end位置-1位置二分查找一个大于或等于num的数字,找到则返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,可以找到数字在数组中的下标。
  • upper_bound()函数:upper_bound( begin,end,num)的含义是从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
  • 不管起点下标是从0开始还是1,都应该减去的是a。

5.2.2 不定长数组:vector

例题5-2:木块问题

思路:

用vector定义一个二位数组,第一维表示是第几堆木块,第二维表示高度,pile[i][j]:表示为第i堆的第j个木块是哪个木块。
直接模拟操作。对于这四种操作,有很多的相似性。所以可以简化一下,不用每一种情况直接模拟。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;const int N  = 30;
//创建n个木块的二维数组
vector<int> pile[N];
int n;
//找到块a,在哪一块上面和他的高度
void find_block(int a,int &p, int &h)
{for(p = 0; p < n; p ++)for(h = 0; h < pile[p].size(); h ++)if(pile[p][h] == a) return;
}
//将p这一堆上面的移走
void clear_above(int p, int h)
{for(int i = h + 1; i < pile[p].size(); i++){int b = pile[p][i];pile[b].push_back(b);//把木块b放回原位 }pile[p].resize(h + 1);//p块只应保留下标0~h的元素 } //将pa这一堆移到pb上面去 void pile_onto(int pa, int ha, int pb){for(int i = ha; i < pile[pa].size(); i ++)pile[pb].push_back(pile[pa][i]);pile[pa].resize(ha);}//输出每一堆的情况。 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]);cout << endl;}}
int main()
{//读入每一个操作 string s1, s2;int a, b;cin >> n;//一开始每个块都只有自己这一块木头 for(int i = 0; i < n; i ++) pile[i].push_back(i);while(cin >> s1 >> a >> s2 >> b){int pa, pb, ha, hb;//对于要操作的a,b,找到他们所在在块的编号和所在块的高度 find_block(a, pa, ha);find_block(b, pb, hb);//如果两个木块在一堆视为非法操作 if(pa == pb) continue; if(s2 == "onto") clear_above(pb, hb);//如果为onto 则将Pb那一堆归位if(s1 == "move") clear_above(pa, ha);//如果为move 则将pa那一堆归位pile_onto(pa, ha, pb);}//输出答案 print();return 0;
}

总结:

5.2.3 集合:set

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

思路:

代码:

#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <sstream>
using namespace std;set<string>dict;//string 集合int main()
{/*stringstream的用法:当读入一个字符串s为"hello".的时候,我们想要的只是hello而不是"hello".所以这个时候,我们只需要把其中的非字母的标点符号全部转成空格。然后用stringstream再对s进行读入。就不会读入空格,只能得到hello */string s, buf;while(cin >> s)//一直读入一个字符串,读到不能读 { //对于字符串s的每一字符判断一下是否是英文字符,是的话就转成小写,不是则转成空格。 for(int i = 0; i < s.length(); i ++)//为什么转小写字母是因为单词都是小写,只有句子首单词才大写首字母。 if(isalpha(s[i]))//如果当前这个字符是英文字符的话 就转成小写字母否则改为空格。 s[i] = tolower(s[i]);else s[i] = ' ';    //转换之后,s就从之前的含有""、.各种标点符号的字符串,转换成一个只有英文字符和空格的字符串。//此时,再用stingstream对s进行重读入即可得到单词。 stringstream ss(s);while(ss >> buf) dict.insert(buf); }  for(auto a : dict) cout << a << endl;return 0;
}

总结:

  • 其中stringstream的用法很关键,需要掌握。
  • set里面的数据自动从小到大排序。
  • isalpha()函数:isalpha(s[i])判断s[i]是否是英文字符。
  • tolower()函数:tolower(s[i])将s[i]转成小写字母。

5.2.4 映射:map

例题5-4:反片语

思路:

把每一个单词标准化,全部转为小写字母后再排序,然后再放到map中统计。
一个单词能通过字母重排得到另外一个单词 当且仅当 这两个单词 标准化之后 是相同的。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
using namespace std;vector<string>words;//存储每一个单词
map<string,int>cnt;//单词标准化之后的映射 //将字符串s 标准化,转成小写字母排序
string repr(string s)
{for(int i = 0; i < s.length(); i ++)s[i] = tolower(s[i]);sort(s.begin(), s.end());return s;
}
int main()
{int n = 0;string s;while(cin >> s){if(s[0] == '#') break;words.push_back(s);string r = repr(s);//将当前单词标准化.//   if(!cnt.count(r)) cnt[r] = 0;cnt[r] ++;//标准化之后的放入map计数 }vector<string>ans;//存储答案for(int i = 0; i < words.size(); i ++){  //如果当前这个单词标准化之后只出现一次, if(cnt[repr(words[i])] == 1) ans.push_back(words[i]);} //将答案数组排个序 sort(ans.begin(),ans.end());for(int i = 0; i < ans.size();i ++)cout << ans[i] << endl;return 0;
}

总结:

  • 结论:一个单词能通过字母重排得到另外一个单词 当且仅当 这两个单词 标准化之后 是相同的。

5.2.5 栈、队列与优先队列

例题5-5:集合栈计算机

思路

代码

#include <iostream>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <vector>using namespace std;#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())typedef set<int> Set;//把set<int>重定义为Set。
map<Set,int> IDcache;//把集合映射为ID
vector<Set>Setcache;//根据ID取集合 int ID(Set x)
{if(IDcache.count(x)) return IDcache[x];Setcache.push_back(x);return IDcache[x] = Setcache.size() - 1;
}
int main()
{int t;cin >> t;while(t --){stack<int>s;int n;cin >> n;//n个操作 for(int i = 0; i < n; i ++){string op;cin >> op;//读入每次操作的指令。if(op[0] == 'P') s.push(ID(Set()));else if(op[0] == 'D')s.push(s.top());else{//否则都要出站两个集合。 Set x1 = Setcache[s.top()];s.pop();Set x2 = Setcache[s.top()];s.pop();Set x;if(op[0] == 'U') set_union(ALL(x1), ALL(x2), INS(x));//如果是U则把两个集合的并集入栈 if(op[0] == 'I') set_intersection(ALL(x1), ALL(x2), INS(x));//如果是I则把两个集合的交集入栈 if(op[0] == 'A'){x = x2;x.insert(ID(x1));}//不管怎么样都要把x入队 s.push(ID(x));} cout << Setcache[s.top()].size() << endl;}cout << "***" << endl;}return 0;
}

总结

1.巧妙使用宏定义
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
2.set的两个内置函数
set_union():求并集
set_intersection():求交集
3.把每一个集合set分配一个编号。

例题5-6 团体队列

思路:


代码:

#include <iostream>
#include <algorithm>
#include <queue>using namespace std;const int N = 1010, M = 1000010;int teamid[M];int main()
{int n, c = 1;while(cin >> n, n){queue<int>team;//维护队列之间的关系queue<int>person[N];//每个小组的人printf("Scenario #%d\n", c++);//每个数据输出一个//处理输入 for(int i = 0; i < n; i ++){int cnt;cin >> cnt;while(cnt --){int x;cin >> x;teamid[x] = i;//第x个人的属于第i组      }} string command;//命令while(cin >> command, command != "STOP"){if(command == "ENQUEUE"){int x;cin >> x;int tid = teamid[x];//得到第x个人的小组编号if(person[tid].empty()) team.push(tid);person[tid].push(x);}else{int tid = team.front();//取第一个小组的编号.auto &q = person[tid];cout << q.front() << endl;q.pop();if(q.empty()) team.pop(); }} cout << endl;}return 0;
}

总结:

  • 队列插队模型。开辟M个队列维护同一个小组之间的关系,再开一个队列维护小组之间的关系。

例题5-7:丑数

思路:

通过唯一分解定理可以证明。所有的丑数 N=2c1∗3c2∗5c3N = 2^{c_1} * 3^{c_2} * 5^{c_3}N=2c1​∗3c2​∗5c3​。

代码:

#include <iostream>
#include <set>
#include <queue>using namespace std;typedef long long ll;
int a[3] = {2, 3, 5};//每次乘2  乘3  乘5
int main()
{priority_queue<ll , vector<ll>, greater<ll>> heap;//优先队列set<ll>s;//有时候会得到重复的数据heap.push(1);s.insert(1);for(int i = 1; ;i ++){ll x = heap.top();heap.pop();if(i == 1500)//第1500出队的数就是答案{cout << "The 1500'th ugly number is " << x << ".\n";break;}for(int j = 0; j < 3; j ++){ll x2 = x * a[j];if(!s.count(x2))//判重{heap.push(x2);s.insert(x2);}}}return 0;}

总结:

  • 用集合set判重是一个重要的方法。

5.3 大整数类

就是高精度算法。

5.4 竞赛题目举例

例题5-8: Unixls命令

思路:


注意本题是竖着输出的。

代码:

#include <iostream>
#include <algorithm>
#include <string>using namespace std;const int maxcol = 60;
const int maxn = 100 + 5;string filename[maxn];void print(string s, int len, char ch)
{cout << s;for(int i = 0; i < len - s.length(); i ++)cout << ch;
}
int main()
{int n;while(cin >> n){   //求最大的长度M int M = 0;//读入每个文件的名字 for(int i = 0; i < n; i ++){cin >> filename[i];M = max(M, (int)filename[i].length());}print("-", 60, '-');//输出函数cout << endl; int col = (maxcol - M) / (M + 2) + 1;//得到总列数。 int row = (n + col - 1) / col;//得到总行数 向上取整。 sort(filename, filename + n);//将单词排序//输出对应的单词 二维转化成一维  for(int r = 0; r < row; r ++){   for(int c = 0; c < col; c ++){int idx = c * row + r;//将二维坐标转成一维坐标if(idx < n) print(filename[idx], c == col - 1 ? M : M + 2, ' ');//如果存在就输出 } cout << endl; }}return 0;
}

总结:

  • 有些时候用输出函数会更加简洁。
  • 二维数组下标转换成一维数组下标(下标都从0开始):a[i][j] = a[j * row + i](竖着) a[i][j]=a[i*col+j](横着)

例题5-9:数据库

思路:

代码:

#include <bits/stdc++.h>using namespace std;int n, m;
string s, st;
map<string, int>idmp;//将字符串->idint getID(string s)
{if(idmp.find(s) == idmp.end()) idmp.insert({s, idmp.size()});return idmp[s];
}
int main()
{while(cin >> n >> m){getchar();//读入回车符vector<int>table[n + 1];idmp.clear();//初始化for(int i = 0; i < n; i ++){getline(cin, s);stringstream input(s);//定义一个stringstream流while(getline(input, st, ',')) table[i].push_back(getID(st));  } bool flog = true;for(int i = 0; i < m - 1 && flog; i ++)for(int j = i + 1; j < m && flog;  j ++){map<pair<int,int>,int>pos;//两列对应字符串标号->行for(int k = 0; k < n && flog; k ++)//遍历每一行 {if(pos.find({table[k][i], table[k][j]}) == pos.end()) pos[{table[k][i], table[k][j]}] = k;else{printf("NO\n%d %d\n%d %d\n", pos[{table[k][i],table[k][j]}]+1, k+1, i+1, j+1);isPNF = false;}} }if(flog) printf("YES\n");}return 0;}

总结:

  • 本题值得注意的是用map实现将每个字符串赋值一个ID。

例题5-10:PGA巡回赛的奖金

思路:

代码:

总结:

留个坑。

第五章 C++与STL入门 例题相关推荐

  1. 【正点原子STM32连载】第五章 STM32基础知识入门 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1

    1)实验平台:正点原子MiniPro H750开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=677017430560 3)全套实验源码+手册+视频 ...

  2. python第五章课后作业_python 入门到实践第五章课后练习

    原博文 2020-06-19 11:05 − users = ['admin','alex','eric','crystol','joji']#创建一个列表for i in users:#遍历整个列表 ...

  3. 计算机原理存储器课件,计算机原理第五章存储器课件.ppt

    计算机原理第五章存储器课件.ppt 教材例题讲解 图5.33 全译码法8088与6264构成32KB存储空间 图5.34 部分译码法8088与6116构成8KB存储空间 图5.35 8088与ROM/ ...

  4. Python编程从入门到实践第五章部分习题

    Python编程从入门到实践第五章部分习题 5-8 5-9` names = ['admin','zhang','li','zhao','song'] for name in names:if nam ...

  5. 《Python从入门到实践》读书笔记——第五章 if语句

    <Python从入门到实践>读书笔记--第五章 if语句 1. 一个简单示例 cars = ['audi', 'bwm', 'subaru', 'toyota']for car in ca ...

  6. 破解完全入门篇,第五章--破解原理

    第五章--破解原理 从本章开始,我们来一步一步学习Crack软件(80%读者昏死过去,且不省人世...另有20%在寻找附近可以用来打人的东西) 不可不说一下学习破解的三个阶段: 初级,修改程序,用ul ...

  7. 零基础入门转录组分析——第五章(表达定量)

    零基础入门转录组分析--第五章(表达定量) 目录 零基础入门转录组分析--第五章(表达定量) 1. 序列比对结果的复查 2. 表达定量 3. 提取有效信息 4. 合并多个样本定量结果 5. 进一步修改 ...

  8. core组件进阶(Opencv3编程入门 第五章) 第四节 图像对比度亮度调整

    core组件进阶(Opencv3编程入门 第五章) 第四节 图像对比度亮度调整 #include <opencv2/core/core.hpp> #include <opencv2/ ...

  9. ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第五章_ArcCatalog使用 - ArcGIS知乎-新一代ArcGIS问答社区 1 ArcCatalog使用 1.1 GIS数据 地理信息系统, ...

最新文章

  1. 解决ERROR 2003 (HY000): Can't connect to MySQL server on host (111)
  2. android device monitor命令行窗口在哪里_Vulkan在Android使用Compute shader
  3. 某些列满足特定条件,然后改变另外的某些列的值(Python)
  4. 基于.net的分布式系统限流组件
  5. Java Spring里com.mysql.jdbc.Driver的加载时机
  6. 远程调用 Spring Cloud Feign
  7. 10问10答:你真的了解线程池吗?
  8. 页面图片延时加载(附实例下载)
  9. 实战演练:如何用BBED恢复删除的数据
  10. java堆栈类在哪个包里_Java堆栈Stack类的使用
  11. Linux内核分析之搭建Mykernel
  12. 华为设备配置备份,配置文件导入导出,设备配置恢复,设备镜像上传
  13. dht11温湿度传感器工作原理引脚功能电路接线图
  14. jboss不打印日志的问题------:logback + slf4j + jboss + spring mvc----菩提树下的杨过
  15. 将NX,JT, step等一些常见3维格式文件直接发布到网页上,可在线浏览
  16. 让 Vivado有Zybo Board的配置文件
  17. 德国研究生分数计算机,申请德国研究生看什么成绩
  18. 让“猪场”名副其实的网易味央,是如何玩转“互联网+养猪”的?
  19. 什么是 DDoS 攻击?- DDoS 含义
  20. 图论(图、树基本知识)

热门文章

  1. 当前安全设置禁止运行该页中activeX控件,该页无法正常显示
  2. 我是才学网络工程的,希望各位多多支持
  3. 三维空间曲线参数方程曲率半径公式
  4. 广州大学计算机网络实验2,计算机网络实验一_
  5. 达内C++21天培训视频教程
  6. CPU数据预取对软件性能的影响
  7. iosepub阅读器_iOS苹果手机上最好的3个mobi阅读器
  8. (一)、Spring Security OAuth2 五种授权方式介绍
  9. 联通的无限流量套餐,比移动好很多
  10. 成都自传-MoccA and 555