库函数专辑

四舍五入

round

round函数用于四舍五入浮点数到最接近的整数。如果参数的小数部分是.5,则这个函数会将数值四舍五入到最近的偶数整数。这是为了遵守IEEE浮点数的标准,减少四舍五入操作的累积误差。

  • 语法: double round(double x);
  • 例子: round(2.3) 返回 2.0, round(3.5) 返回 4.0, round(4.5) 返回 4.0

向下取整

floor

floor函数将浮点数向下取整到最接近的整数,但不大于原数。无论原数的小数部分是多少,都会被丢弃,仅保留整数部分。

  • 语法: double floor(double x);
  • 例子: floor(2.3) 返回 2.0, floor(-3.8) 返回 -4.0

向上取整

ceil

ceil函数将浮点数向上取整到最近的整数,但不小于原数。这意味着它会舍弃原数的小数部分,并在有小数的情况下将整数部分加一。

  • 语法: double ceil(double x);
  • 例子: ceil(2.3) 返回 3.0, ceil(-3.8) 返回 -3.0

幂运算

pow

用于计算一个数的指数幂。

应用场景:计算一个电子的电位能量,给定电荷量(Q)和电场强度(E)的情况下,电位能量(U)可以使用公式U = Q * E * d计算,其中d是电场中的位置深度,假设我们需要计算d的平方对结果的影响。

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    double Q = 1.6e-19; // 电荷量,单位库仑
    double E = 500; // 电场强度,单位牛顿/库仑
    double d = 0.05; // 位置深度,单位米
    double U = Q * E * pow(d, 2); // 计算d的平方对U的影响

    cout << "Potential Energy: " << U << " Joules" << endl;
    return 0;
}

平方根

sqrt

应用场景:计算直角三角形的斜边长度,给定两条直角边的长度。

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    double a = 3.0, b = 4.0;
    double c = sqrt(pow(a, 2) + pow(b, 2)); // 根据勾股定理计算斜边长度

    cout << "Hypotenuse: " << c << endl;
    return 0;
}

对数

log2 和 log10

计算二进制表示所需的位数

log2 函数计算以2为底的对数,这在需要处理与二进制相关的问题时非常有用。比如,计算一个正整数在二进制表示中需要多少位。这对于算法竞赛中的位操作题目,比如求解一个数的二进制中1的数量,或者需要用位掩码表示某些状态时,非常实用。

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Enter a positive integer: ";
    cin >> n;

    int bitsNeeded = log2(n) + 1; // 加1因为log2(n)计算的是索引,而位数从1开始计数

    cout << "Bits needed for binary representation of " << n << ": " << bitsNeeded << endl;
    return 0;
}

这个例子假设n是一个正整数。log2(n)计算了n在二进制表示下的最高位的位置(从0开始计数),因此需要加1来得到总位数。

计算十进制数中的位数

log10 函数计算以10为底的对数,它可以用来快速确定一个数在十进制表示中的位数。这在处理需要数位操作的算法问题时特别有用,比如数字反转、数位和计算等。

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Enter a positive integer: ";
    cin >> n;

    int digits = log10(n) + 1; // 加1因为log10(n)计算的是最高位的索引

    cout << "Digits in the decimal representation of " << n << ": " << digits << endl;
    return 0;
}

这个例子演示了如何快速计算出一个正整数在十进制表示中的位数。类似于log2的用法,这里log10(n)计算的是n的最高位的位置索引,在十进制中,这个位置索引加1就是该数的总位数。

其他

modf - 分解浮点数

应用场景:如果要将一个浮点数的小时表示转换为小时和分钟(例如,将3.75小时转换为3小时45分钟),可以使用modf

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    double hours;
    cin >> hours;
    double intpart, fracpart;
    fracpart = modf(hours, &intpart);
    int minutes = round(fracpart * 60);
    cout << intpart << " hours and " << minutes << " minutes" << endl;
    return 0;
}

fmod - 浮点数除法的余数

应用场景:计算一个飞轮每分钟转动7.5圈,经过98分钟后,它总共转了多少圈,并计算其相对于整圈的余数(即最后停留的位置)。

#include <cmath>
#include <iostream>
using namespace std;

int main() {
    double rotationsPerMinute = 7.5;
    double timeInMinutes = 98;
    double totalRotations = rotationsPerMinute * timeInMinutes;
    double remainder = fmod(totalRotations, 1); // 计算相对于整圈的余数

    cout << "Remainder: " << remainder << " of a rotation" << endl;
    return 0;
}

输入

getline

  • 用法getline(cin, stringVar);
  • 目的:读取一整行文本,直到遇到换行符\ngetline会丢弃换行符,但会读取并保留行中的其他所有字符,包括空格
  • 场景:当你需要读取包含空格的一行文本或者确保一次读取直到行末尾时,getline是最佳选择。
  • 注意:在getline之前使用cinscanf读取其他数据,并且期望紧接着用getline读取下一行时,需要注意消耗掉留在输入缓冲区中的换行符。

如:家族

cin

  • 用法cin >> variable;
  • 目的:读取数据,使用空格、制表符或换行符作为分隔符。cin会自动忽略任何前导空白字符。
  • 场景:适用于读取分隔开的单个数据项,如整数、浮点数、字符串(不含空格)。
  • 优点:使用方便,支持连锁调用(如cin >> a >> b;)。
  • 缺点:不能读取含有空格的字符串。

scanf

  • 用法scanf("%format_specifier", &variable);
  • 目的:根据指定的格式读取数据。scanf提供了格式化输入的能力,可以按照特定的格式读取数据。
  • 场景:当输入数据遵循固定格式,且需要精确控制输入格式时使用。它对于读取复杂格式化的输入非常有效。
  • 优点:读取速度快,可以精确地控制输入数据的格式。
  • 缺点:使用不当容易出错,如忽略返回值可能会导致未定义行为。对于字符串的处理不如cingetline直观。

常用格式控制

整数

  • %d:读取一个十进制整数。
  • %ld%lld:分别用于读取longlong long类型的十进制整数。
  • %u:读取一个无符号十进制整数。
  • %x%X:读取一个十六进制整数。
  • %o:读取一个八进制整数。

浮点数

  • %f:读取一个浮点数(floatdouble)。
  • %lf:读取一个double类型的浮点数。虽然%fprintf中用于double,但在scanf%f是用于float的,%lf用于double

字符和字符串

  • %c:读取一个字符。
  • %s:读取一个字符串,直到遇到空白字符(空格、制表符或换行符)为止。读取的字符串自动以空字符\0结尾。

其他

  • %p:读取一个指针。
  • %%:读取一个%字符。

读取多个值

scanf可以在一个调用中读取多个值,格式说明符之间的空格将被忽略,输入中的空格、制表符和换行符可以在任何格式说明符之间进行匹配。

示例

int a, b;
scanf("%d %d", &a, &b);

char ch;
scanf(" %c", &ch); // 注意前面的空格,用于跳过前面的空白字符

double d;
scanf("%lf", &d);

char str[100];
scanf("%s", str); // 不需要&,因为数组名本身就是地址

注意事项

  • 当使用%s读取字符串时,确保目标数组足够大,以避免缓冲区溢出。
  • 使用%c读取字符时,如果想要忽略前面的空白字符(包括空格、制表符和换行符),可以在%c之前加一个空格,如scanf(" %c", &ch);
  • 对于scanf来说,必须提供变量的地址作为参数(使用&运算符),除了字符串数组因为数组名已经是地址。
  • 使用scanf时要特别注意返回值,它返回成功读取的项目数。这个返回值可以用来检测输入是否按照预期进行。

getchar

  • 用法int c = getchar();
  • 目的:从标准输入读取下一个字符,并返回它。如果遇到文件结束符(EOF),则返回EOF
  • 场景:适用于需要逐字符读取输入时,如处理输入流中的空格、换行符等特殊字符。

总结

在算法竞赛中,选择哪种输入方法取决于具体任务的需求:

  • 如果你需要读取整行数据,特别是包含空格的字符串,使用getline
  • 对于分隔的数据项,特别是不包含空格的字符串或数字,cin是一个方便的选项。
  • 当输入格式非常具体,或者在性能极其关键的情况下,scanf可能是最好的选择,尽管它需要更多的注意来避免错误。
  • 读取单个字符,逐字符处理,使用getchar

输出

cout

  • 用法cout << value << ...;
  • 目的:输出数据到标准输出。cout是C++中的标准输出流对象。
  • 场景:适用于大多数输出需求,特别是当需要输出字符串、数字或是其他复合数据结构时。cout由于是类型安全的,因此在处理类似于字符串和数字混合输出时非常方便。
  • 优点
    • 类型安全,自动类型推断。
    • 支持连锁调用,易于使用。
    • 可以与C++标准库中的其他流对象一起使用,如文件流。
  • 缺点
    • 相比于printf,在处理格式化输出时可能不那么灵活。
    • 在某些情况下,可能比printf慢,尤其是没有优化I/O性能时。

使用 ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);进行优化

printf

  • 用法printf("format specifier", value1, value2, ...);
  • 目的:根据指定的格式输出数据。printf是C语言中的标准输出函数,但在C++中仍然可用。
  • 场景:当需要精确控制输出格式,比如指定浮点数的精度,或者输出的宽度时。
  • 优点
    • 高度格式化的输出。
      • 在某些情况下,性能优于cout
  • 缺点
    • 不是类型安全的,错误的格式说明符可能导致运行时错误

常用格式控制

printf非常强大,支持多种格式控制符,可以精确地控制输出格式。

  • %d:输出十进制整数。
  • %u:输出无符号十进制整数。
  • %f:输出浮点数,默认情况下保留小数点后六位。
  • %.2f:输出浮点数,小数点后保留两位。
  • %s:输出字符串。
  • %c:输出单个字符。
  • %x%X:输出十六进制数,x产生小写字母,X产生大写字母。
  • %p:输出指针地址。
  • %%:输出%字符。

高级用法

  • 指定宽度%5d表示输出的整数至少占5个字符宽度,如果数字位数不够,前面补空格。
  • 左对齐%-5d表示输出的整数左对齐,至少占5个字符宽度。
  • 指定浮点数精度%.3f表示输出的浮点数保留三位小数。
  • 动态宽度和精度%*.*f允许动态指定宽度和精度,这两个值由额外的参数提供。

例如:

printf("%5d\n", 123);  // "  123"
printf("%-5d\n", 123); // "123  "
printf("%.3f\n", 3.1415926); // "3.142"

putsputchar

  • puts
  • 用法puts(const char* s);
  • 目的:输出字符串s到标准输出,并自动在末尾添加换行符。
  • 优点:简单易用,自动添加换行符,适合快速输出一行字符串。
  • 缺点:只能输出字符串,不能格式化输出其他类型的数据。
  • putchar
  • 用法putchar(int char);
  • 目的:输出单个字符到标准输出。
  • 优点:非常简单,用于输出单个字符。
  • 缺点:每次只能输出一个字符,不适用于复杂的输出需求。

总结

根据需要输出的数据类型和格式化要求来决定。cout适合大部分场景且易于使用;printf在需要特定格式时更为强大;而putsputchar适用于简单的输出需求,特别是当性能要求较高时。

常用算法

unique

在C++中,unique 函数是一个非常有用的工具,它用于移除序列中的连续重复元素,通常与 sort 函数结合使用来移除所有重复元素。unique 函数是 <algorithm> 头文件的一部分。下面是如何使用 unique 函数的详细介绍:

基本用法

unique 函数通常接受两个迭代器作为参数,表示要处理的序列的开始和结束。它将连续的重复元素移至序列的末尾,并返回一个迭代器,指向去除重复元素后序列的新逻辑末尾。

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
   std::vector<int> v = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};

   // 使用 unique 函数
   auto it = std::unique(v.begin(), v.end());

   // 移除重复之后,需要调用 erase 来缩减容器大小
   v.erase(it, v.end());

   for (int x : v) {
       std::cout << x << " ";
   }

   return 0;
}

sort 结合使用

如果要移除一个序列中所有的重复元素,首先需要对序列进行排序。unique 只能移除相邻的重复元素。

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
   std::vector<int> v = {4, 2, 2, 3, 3, 1, 4, 4};

   // 首先排序
   std::sort(v.begin(), v.end());

   // 然后使用 unique
   auto it = std::unique(v.begin(), v.end());

   // 移除重复之后,需要调用 erase 来缩减容器大小
   v.erase(it, v.end());

   for (int x : v) {
       std::cout << x << " ";
   }

   return 0;
}

自定义比较函数

unique 也可以接受第三个参数,这是一个比较函数,用于定义什么构成重复。这允许在去重时使用自定义的比较逻辑。

#include <algorithm>
#include <vector>
#include <iostream>

bool customCompare(int a, int b) {
   return a == b;
}

int main() {
   std::vector<int> v = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};

   // 使用自定义比较函数
   auto it = std::unique(v.begin(), v.end(), customCompare);

   // 移除重复之后,需要调用 erase 来缩减容器大小
   v.erase(it, v.end());

   for (int x : v) {
       std::cout << x << " ";
   }

   return 0;
}

lower_bound

在C++中,lower_bound 是一个非常有用的标准库函数,它用于在已排序的序列中找到第一个不小于(即大于或等于)给定值的元素的位置。这个函数定义在 头文件中,并可以与任何类型的迭代器一起使用,如数组、向量等。以下是 lower_bound 的基本用法和一些高级应用。

基本用法

lower_bound 函数通常接受四个参数:序列的开始迭代器、结束迭代器、要查找的值和可选的比较函数。如果不提供比较函数,则使用元素的默认小于操作符进行比较。

下面是使用 lower_bound 的一个示例:

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
   std::vector<int> v = {1, 3, 5, 7, 9};

   // 查找第一个不小于 6 的元素
   auto it = std::lower_bound(v.begin(), v.end(), 6);

   if (it != v.end()) {
       std::cout << "The first element not less than 6 is: " << *it << std::endl;
   } else {
       std::cout << "No element not less than 6" << std::endl;
   }

   return 0;
}

与上界结合

lower_bound 可以与 upper_bound 结合使用,以找到一个特定范围,其中包括所有等于给定值的元素。upper_bound 查找第一个大于指定值的元素的迭代器。

#include <algorithm>
#include <vector>
#include <iostream>

int main() {
   std::vector<int> v = {1, 2, 2, 3, 3, 3, 4, 4};

   // 查找值为 3 的范围
   auto lb = std::lower_bound(v.begin(), v.end(), 3);
   auto ub = std::upper_bound(v.begin(), v.end(), 3);

   std::cout << "Elements equal to 3: ";
   for (auto it = lb; it != ub; ++it) {
       std::cout << *it << " ";
   }
   std::cout << std::endl;

   return 0;
}

使用自定义比较函数

如果你的数据结构较为复杂,或者你想根据某些自定义的规则进行元素比较,你可以向 lower_bound 传递一个比较函数。

#include <algorithm>
#include <vector>
#include <iostream>

bool compare(int a, int b) {
   return a < b;
}

int main() {
   std::vector<int> v = {1, 3, 5, 7, 9};

   // 使用自定义比较函数查找第一个不小于 4 的元素
   auto it = std::lower_bound(v.begin(), v.end(), 4, compare);

   if (it != v.end()) {
       std::cout << "The first element not less than 4 is: " << *it << std::endl;
   } else {
       std::cout << "No element not less than 4" << std::endl;
   }

   return 0;
}

⬅️ 计算几何 🏠 00-刷题理模型 ➡️ 10、星际探险