本文主要总结了 C++ ACM 编程模式的代码模板与常见的输入/输出处理。
目录:
代码模板
/* ----------------- 头文件 ----------------- */
// C
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>
#include <ctime>
#include <cmath>
// C++
// IO
#include <iostream>
#include <iomanip>
#include <sstream>
// 算法
#include <algorithm>
#include <numeric>
// 容器
#include <vector>
#include <list>
#include <string>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <utility>
#include <bitset>
// 其他
#include <functional>
#include <random>
using namespace std;
/* ----------------- IO 加速 ----------------- */
#define FastIO ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr);
/* ----------------- 定义别名 ----------------- */
using ll = long long;
using ull = unsigned long long;
using db = double;
using ld = long double;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
using pull = pair<ull, ull>;
/* ----------------- 常量定义 ----------------- */
const double EPS = 1e-6;
const int INF = 1 << 29;
const int MOD = 1e9 + 7;
const int MAX_N = 100;
/* ----------------- debug 输出 ----------------- */
string to_string(const string &s) { return '"' + s + '"'; }
string to_string(const char *s) { return to_string((string) s); }
string to_string(char c) { return '\'' + string(1, c) + '\''; }
string to_string(bool b) { return (b ? "true" : "false"); }
template<typename A, typename B>
string to_string(pair<A, B> p) { return "(" + to_string(p.first) + ", " + to_string(p.second) + ")"; }
template<typename T>
string to_string(T t) {
bool first = true;
string res = "{";
for (const auto &x: t) {
if (!first) res += ", ";
first = false;
res += to_string(x);
}
res += "}";
return res;
}
void debug() { cerr << endl; }
template<typename Head, typename... Tail>
void debug(Head H, Tail... T) {
cerr << to_string(H) << " ";
debug(T...);
}
/* ----------------- 字符串分割 ----------------- */
vector<string> split(const string &str, const char &delim) {
vector<string> res;
stringstream ss(str);
string tmp;
while (getline(ss, tmp, delim)) { if (!tmp.empty()) res.push_back(std::move(tmp)); }
return res;
}
// 示例
int main() {
// 启用 IO 加速
// 注意: 启用后不能混用 cin/cout 与 scanf/printf
FastIO
int a = 3;
db b = 3.45;
ld c = 123.456;
char d = 'x';
string e = "123";
// 输出: 3 3.450000 123.456000 'x' "123"
debug(a, b, c, d, e);
pii p1 = make_pair(1, 2);
pll p2 = make_pair(LONG_LONG_MIN, LONG_LONG_MAX);
pull p3 = make_pair(ULONG_LONG_MAX, ULONG_LONG_MAX);
// 输出: (1, 2) (-9223372036854775808, 9223372036854775807) (18446744073709551615, 18446744073709551615)
debug(p1, p2, p3);
vector<string> arr;
string str = "123,456,789";
// 字符串分割
arr = split(str, ',');
unordered_set<int> s;
s.insert(1);
s.insert(2);
unordered_map<int, string> mappings;
mappings[1] = "hello";
mappings[2] = "world";
// 输出: {"123", "456", "789"} {2, 1} {(2, "world"), (1, "hello")}
debug(arr, s, mappings);
return 0;
}
基本输入
单组输入
int main() {
int a, b;
cin >> a >> b;
cout << a + b << '\n';
return 0;
}
多组输入 - 固定数量
示例:A+B(2)
int main() {
int t, a, b;
cin >> t;
while (t--) {
cin >> a >> b;
cout << a + b << '\n';
}
}
多组输入 - 直到读至输入文件末尾
示例:A+B(1)
int main() {
int a, b;
while (cin >> a >> b) {
cout << a + b << '\n';
}
return 0;
}
多组输入 - 直到读至特殊输入
示例:A+B(3)
int main() {
int a, b;
while (cin >> a >> b && (a != 0 || b != 0)) {
cout << a + b << '\n';
}
return 0;
}
字符串输入
单个字符串不含默认分隔符(空格、制表符、回车换行)
int main() {
string a, b;
cin >> a >> b; // 输入: 123 456
cout << a << " " << b; // 输出: 123 456
}
单个字符串含有空格、制表符,但不含回车换行
string a;
cin >> a; // 输入: how are you!
cout << a; // 输出: how
string b;
getline(cin, b); // 输入: how are you!
cout << b; // 输出: how are you!
逐个字符输入
std::istream::get
:从流中提取字符,作为未格式化的输入。
函数签名:
// single character (1)
// 从流中提取单个字符
int get();
istream& get (char& c);
// c-string (2)
// 从流中提取字符并将它们作为 c 字符串存储在 s 中,
// 直到提取 (n-1) 个字符或遇到分隔符:分隔符是换行符 ('\n') 或 delim(如果指定了此参数)
// 如果找到分隔符,则不会从输入序列中提取,而是保留在那里作为要从流中提取的下一个字符
istream& get (char* s, streamsize n);
istream& get (char* s, streamsize n, char delim);
// stream buffer (3)
// 从流中提取字符并将它们插入到由流缓冲区对象 sb 控制的输出序列中,
// 一旦这样的插入失败或在输入序列中遇到定界字符(定界字符是换行符)就停止,
// 如果指定了 delim 参数,则为字符 delim
istream& get (streambuf& sb);
istream& get (streambuf& sb, char delim);
示例:
// single character (1)
char c;
while (c = cin.get()) {...}
while (cin.get(c)) {...}
// c-string (2)
char ch[20];
cin.get(ch, 10, '.'); // 读取前 9 个字符或直到分隔符 '.'
单行输入 - 固定单行字符串数量
示例:字符串排序(1)
int main() {
int t;
cin >> t;
vector<string> arr;
string tmp;
while (t--) {
cin >> tmp;
arr.push_back(std::move(tmp));
}
...
return 0;
}
多行输入 - 不定单行字符串数量
示例:字符串排序(2)
int main() {
string str;
vector<string> arr;
while (cin >> str) {
arr.push_back(std::move(str));
if (cin.get() == '\n') {
sort(arr.begin(), arr.end());
for (const auto &s : arr) cout << s << ' ';
cout << '\n';
arr.clear();
}
}
return 0;
}
多行输入 - 单行含分隔符
示例:字符串排序(3)
vector<string> split(const string &str, const char &delim) {
vector<string> res;
stringstream ss(str);
string tmp;
while (getline(ss, tmp, delim)) { if (!tmp.empty()) res.push_back(std::move(tmp)); }
return res;
}
int main() {
string str;
while (getline(cin, str)) {
vector<string> arr = split(str, ',');
sort(arr.begin(), arr.end());
for (int i = 0; i < arr.size(); ++i) {
if (i != arr.size() - 1) {
cout << arr[i] << ',';
} else cout << arr[i] << '\n';
}
}
return 0;
}
格式化输出
ACM 编程模式的输入和输出是相互独立的,故每当处理完一组测试数据,就应当按照题目要求进行相应的输出操作,而不必将所有输出结果保存起来一起输出。
不同进制输出
eg 1. C++ cout
int val = 15;
// 默认算子 *dec - 以 十进制 形式输出整数
cout << val << '\n';
// 流操作算子 oct - 以 八进制 形式输出整数
cout << oct << val << '\n'; // 17
// 流操作算子 hex - 以 十六进制 形式输出整数
cout << hex << val << '\n'; // f
// 利用 bitset 将其转换为 二进制 形式输出整数
cout << bitset<sizeof(int) * 8>(val) << '\n'; // 32 bit: 00000000000000000000000000001111
cout << bitset<8>(val) << '\n'; // 8 bit : 00001111
eg 2. C printf
int val = 15;
printf("%d\n", val); // 十进制 15
printf("%o\n", val); // 八进制 17
printf("%x\n", val); // 十六进制 f
保留指定的有效位数与小数
eg 1. C++ cout
// 注意:以下保留操作,末位均会四舍五入
double val = 1234.456789;
// 默认保留 6 位有效数字
cout << val << '\n'; // 1234.46
// setprecision(n) 设置保留 n 位有效数字
cout << setprecision(8) << val << '\n'; // 1234.4568
// a. fixed + setprecision(n) 设置保留小数点后 n 位,如果 n > 小数点位数,则添 0 补齐
cout << fixed << setprecision(2) << val << '\n'; // 1234.46
cout << fixed << setprecision(10) << val << '\n'; // 1234.4567890000
// b. 预先设置
cout.setf(ios::fixed);
cout << setprecision(2);
cout << val << '\n'; // 1234.46
注意: 使用流算子之后,会对后面所有操作生效。
eg 2. C printf
// 注意:以下保留操作,末位均会四舍五入
double val = 1234.123456789;
// 默认保留小数点后 6 位
printf("%f\n", val); // 1234.123457
// 通过 %.nf 格式化控制符,设置保留小数点后 n 位,如果 n > 小数点位数,则添 0 补齐
printf("%.2f\n", val); // 1234.12
printf("%.10f\n", val); // 1234.1234567890
格式化输出宽度
eg 1. C++ cout
int a = 1, b = 12;
// 设置输出宽度 5,默认右对齐
cout << setw(5) << a << endl;
cout << setw(5) << b << endl;
// 输出
// 1
// 12
// 设置输出宽度 5,不足位数用 * 补齐
cout << setfill('*') << setw(5) << a << endl;
cout << setfill('*') << setw(5) << b << endl;
// 输出
// ****1
// ***12
eg 2. C printf
int a = 1, b = 12;
// 设置输出宽度 5,右对齐
printf("%5d\n", a);
printf("%5d\n", b);
// 输出
// 1
// 12
// 设置输出宽度 5,不足位数用 0 补齐
printf("%05d\n", a);
printf("%05d\n", b);
// 输出
// 00001
// 00012