一道简单题,主要考察小数点保留,需要用到<sstream>和<iomanip>两个包,因此主要记录这两个包的使用,以及对比常规方法。
1. 题目解析
题目给了三个条件,分别是电脑数n、每单位时间充电量x和电脑电池容量a[i],规定闪充为每单位时间充电4x,要求所有电脑的电量都为零的前提下,使用闪充给所有电脑充满电,计算需要的总充电时间并保留结果的小数点后两位。
思路很简单明了:
- 求所有电脑使用闪充充满所需的时间;
- 对总时间进行截断,保留小数点后两位;
- 输出结果。
2. 思路解析
求充电时间是没什么难度的,直接所有电脑电池容量相加除以4x就行了,这题主要的考点在于对小数保留有效位数的处理。
一开始我打算用传统的处理方法,即将float或double类型的总时间转为string类型,读取字符截断小数点两位后的数字。然而提交的时候有两个用例出现了问题:计算结果为5.625的用例,保留两位是直接舍去的5.62,而6.9375的用例,保留两位却是进一到6.94。
百思不得其解,于是我向一起学习的朋友请教,得到了答案:像5.625这样的十进制小数在二进制中不能被精确表示,所以它会被存储为一个非常接近5.625的浮点数。在具体的实现中,5.625 可能会变成类似于 5.624999999...,而不是精确的 5.625。因此,当我们对这个近似值进行两位小数的截取或四舍五入时,系统认为这个值实际上更接近于5.62 而不是 5.63,导致显示结果为5.62。
因此,对于计算或输入的、处于四舍五入临界点的小数,我们不能自己编写常规的判断方法。这时候就需要请出我们的强力援手了——<sstream>包和<iomanip>包。
<sstream>:提供了 字符串流 类stringstream,主要用于将字符串和其他类型的数据进行便捷的输入输出、格式化处理。本次所用的主要是输出流ostringstream。<iomanip>:提供了多种用于格式化输出的操纵器(manipulators)。这些操纵器可以影响流(例如cout)的输出格式,常用于控制数字、文本的显示方式。本次主要用于对<sstream>的输出流进行有效小数位的处理。
3. 代码实现
思路清晰后,代码实现就非常简单了:
string solution(int n, int x, vector<int> a) {
float time = 0;
for (int i = 0; i < n; i++) {
time += (float)a[i] / x / 4;
}
// 将 float 转换为字符串并保留小数点后两位
ostringstream oss;
oss << fixed << setprecision(2) << time;
return oss.str();
}
重点代码为oss << fixed << setprecision(2) << time,其作用如下:
fixed:将浮点数以固定小数点形式显示,与之相对的是scientific:将浮点数以科学计数法形式显示。setprecision(n):设置浮点数的精度。与fixed搭配使用时,n表示小数位数;不与fixed搭配时,表示总有效位数。
因此,该行代码的作用为:将保留两位小数处理的time写入oss中。
4. 性能分析
4.1 时间复杂度
时间复杂度分析主要分为两个部分:
- 主循环部分:该部分完整遍历数组
a,所需时间为 ; - 小数处理部分:仅进行了字符串格式化的操作,所需时间为 。
因此,该算法的总时间复杂度为 。
4.2 空间复杂度
算法仅使用了time和oss这两个常数级变量,因此空间复杂度为 。