1. OJ(online judge)题⽬输⼊情况汇总
在竞赛的 OJ 题⽬中,⼀般关于输⼊场景总结为下⾯四类:
接下来,我们就结合题⽬,给⼤家分别介绍。
1.1 单组测试⽤例
练习1 B2009 计算 (a+b)/c 的值 - 洛谷 | 计算机科学教育新生态 128
cpp
#include <iostream>
using namespace std;
int main()
{
int a, b, c;
//只处理一组数据
cin >> a >> b >> c;
cout << (a + b) / c << endl;
return 0;
}
练习2:B2081 与 7 无关的数 - 洛谷 | 计算机科学教育新生态 128
cpp
#include <iostream>
using namespace std;
int main()
{
int n = 0;
cin >> n;
int i = 1;
int sum = 0;
while (i <= n)
{
if (i % 7 != 0 && i / 10 != 7 && i % 10 != 7)
sum += i * i;
i++;
}
cout << sum << endl;
return 0;
}
1.2 多组测试⽤例
1.2.1 测试数据组数已知
练习1:多组输入a+b II 129
cpp
#include <iostream>
using namespace std;
int main()
{
int n = 0;
int a = 0, b = 0;
cin >> n;
while (n--)
{
cin >> a >> b;
cout << a + b << endl;
}
return 0;
}
练习2:B2064 斐波那契数列 - 洛谷 | 计算机科学教育新生态 129
cpp
#include <iostream>
using namespace std;
const int N = 40;
int arr[N] = {0,1,1};
int n,a;
int main()
{
cin >> n;
for(int i = 3; i <= 30; ++i)
{
arr[i] = arr[i-1] + arr[i-2];
}
while(n--)
{
cin >> a;
cout << arr[a] << endl;
}
return 0;
}
练习3:B3769 [语言月赛202305] 制糊串 - 洛谷 | 计算机科学教育新生态 130
cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s,t;
int q;
int l1,l2,r1,r2;
cin >> s >> t >> q;
while(q--)
{
cin >> l1 >> r1 >> l2 >> r2;
//输入的下标是从1开始的,string substr(size_t pos = 0, size_t len = npos) const
string s1 = s.substr(l1-1,r1-l1+1);
string t1 = t.substr(l2-1,r2-l2+1);
if (s1 < t1) cout << "yifusuyi" << endl;
else if (s1 > t1) cout << "erfusuer" << endl;
else cout << "ovo" << endl;
}
return 0;
}
题⽬中说 "有 q 次询问",意思是程序要处理q组测试数据,(也就是对应 q 次循环),我们
要针对每次询问,给出⼀个结果。 其实就是之前的单组测试变成了 q 组测试,在之前的代码上套⼀层 while 循环即可。当 有 q 次询问的时候, while(q--) 是⾮常⽅便的⽅式。然后就按照单组输⼊的⽅式处理每 组输⼊的数据就好了。
1.2.2 测试数据组数未知
练习1:多组输入a+b 131
cpp
#include <iostream>
using namespace std;
int a, b;
int main()
{
while (cin >> a >> b)
{
cout << a + b << endl;
}
return 0;
}
cin >> a; 会返回⼀个流对象的引⽤,即 cin 本⾝。在 C++ 中,流对象 cin 可以被⽤ 作布尔值来检查流的状态。如果流的状态良好(即没有发⽣错误),流对象的布尔值为 true 。如果发⽣错误(如遇到输⼊结束符或类型不匹配),布尔值为 false 。 在 while (cin >> a >> b) 语句中,循环的条件部分检查 cin 流的状态。如果流成 功读取到2个值, cin >> a >> b 返回的流对象 cin 将被转换为 true ,循环将继 续。如果读取失败(例如遇到输⼊结束符或⽆法读取到2个值), cin >> a >> b 返回的 流对象 cin 将被转换为 false ,循环将停⽌
练习2:数字三角形 131
cpp
#include <iostream>
using namespace std;
int main()
{
int n;
while(cin >> n)
{
//打印行
for(int i = 1; i <=n; ++i)
{
//打印每一行的元素
for(int j = 1; j <= i; ++j)
{
cout << j << " ";
}
cout << endl;
}
}
return 0;
}
练习3:定位查找 132
cpp
#include <iostream>
using namespace std;
const int N = 25;
int arr[N];
int n,m;
int main()
{
while(cin >> n)
{
for(int i = 0; i < n; ++i)
{
cin >> arr[i];
}
cin >> m;
int i = 0;
for(i; i < n; ++i)
{
if (m == arr[i]) {
cout << i << endl;
break;
}
}
//最好在题目中复制
if (i == n) cout << "No" << endl;
}
return 0;
}
1.2.3 特殊值结束测试数据
练习1:字符统计 133
cpp
#include <iostream>
using namespace std;
int l,d,o;
int main()
{
int ch = 0;
while((ch = getchar()) != '?')
{
if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' ) l++;
else if (ch >= '0' && ch <= '9') d++;
else o++;
}
cout << "Letters=" << l << endl;
cout << "Digits=" << d << endl;
cout << "Others=" << o << endl;
return 0;
}
//islower isupper isdigit
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int l,d,o;
int main()
{
int ch = 0;
while((ch = getchar()) != '?')
{
if (islower(ch) || isupper(ch)) l++;
else if (isdigit(ch)) d++;
else o++;
}
cout << "Letters=" << l << endl;
cout << "Digits=" << d << endl;
cout << "Others=" << o << endl;
return 0;
}
//isalpha
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int l,d,o;
int main()
{
int ch = 0;
while((ch = getchar()) != '?')
{
if (isalpha(ch)) l++;
else if (isdigit(ch)) d++;
else o++;
}
cout << "Letters=" << l << endl;
cout << "Digits=" << d << endl;
cout << "Others=" << o << endl;
return 0;
}
//getline
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int l,d,o;
int main()
{
string s;
getline(cin, s);
s.pop_back();//删掉?最后一个字符
for(auto ch : s)
{
if (isalpha(ch)) l++;
else if (isdigit(ch)) d++;
else o++;
}
cout << "Letters=" << l << endl;
cout << "Digits=" << d << endl;
cout << "Others=" << o << endl;
return 0;
}
练习2:多组数据a+b III 134
cpp
#include <iostream>
using namespace std;
int main()
{
int a = 0, b = 0;
//逗号表达式特点
//1.从左向右依次计算
//2.整个表达式结果是最后一个表达式的结果
while (cin >> a >> b, a || b)
{
cout << a + b << endl;
}
return 0;
}
2. 输⼊时特殊技巧
2.1 技巧1:含空格字符串的特殊处理⽅式
根据我们现在掌握的知识,含空格的字符串,如要读取有 fgets 、 scanf 、 getchar 、 getline 四种⽅式解决,但是有时候,根据题⽬的情况,不⼀定⾮要完整的 读取这个带空格的字符串, ⽽是将字符串中空格隔开的每⼀个字符串,当做⼀个单词处理更⽅便,也避免了读取带空格字符串的各种问题。
练习:B2109 统计数字字符个数 - 洛谷 | 计算机科学教育新生态 135
解法1:读取整个带空格的字符串分析
cpp
//getline
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string s;
getline(cin, s);
int c = 0;
for(auto ch : s)
{
if (isdigit(ch)) c++;
}
cout << c << endl;
return 0;
}
解法2:按照多个单词分析
cpp
//cin
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main() {
string s;
int c = 0;
while(cin >> s)
{
for(auto ch : s)
{
if (isdigit(ch)) c++;
}
}
cout << c << endl;
return 0;
}
2.2 技巧2:数字的特殊处理⽅式
当我们程序运⾏的时候,在控制台输⼊ 123 的时候,这时的 123 是三个字符, 123 是⼀个字符序列,程序会根据代码中的数据类型,可能将 123 解析成整型,也可能将 123 解析成字符串。 ⽐如:
cpp
int num = 0;
cin >> num;//输⼊123, 就被解析成整数
string s;
cin >> s; //输⼊123, 就被解析成字符串
这⾥的解析的⽅式,主要是依赖编译器对变量类型的识别,根据类型再将读取字符串数据转化成对应类型的数据。 我们在写代码的时候,应该根据实际的情况,来决定如何处理输⼊的内容。
练习:小乐乐改数字_牛客题霸_牛客网 136
解法1:当做整数读取
cpp
//double pow (double base, double exponent);
#include <iostream>
#include <cmath>
using namespace std;
int main() {
int n;
cin >> n;
int ret = 0;
int i = 0;
while(n)
{
if (n % 10 % 2)
ret += pow(10,i);
n /= 10;
++i;
}
cout << ret << endl;
return 0;
}
解法2:当做字符串处理
cpp
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int main() {
string s;
cin >> s;
for(int i = 0; i < s.size(); ++i)
{
//'0' = 48
//'1' = 49
if (s[i] % 2)
s[i] = '1';
else
s[i] = '0';
}
cout << stoi(s) << endl;
return 0;
}
3. scanf/printf 和 cin/cout的对⽐
scanf 和 printf 是 C 语⾔中的标准输⼊输出函数,⽽ cin 和 cout 是 C++ 语⾔中的标准输⼊输出流对象。它们各⾃有优缺点,整体上来说 cin 和 cout 会更加⽅便,但有时候我们也不得不 使⽤ scanf 和 printf 。
3.1 格式控制差异
·scanf 和 printf 不能⾃动识别输⼊数据的类型,需要⼿动指定格式字符串,容易出现格式错
误。开发者需要确保格式字符串与变量类型匹配,否则会导致未定义⾏为。
·cin 和 cout 会根据变量类型⾃动处理输⼊输出,避免格式化错误。相对 scanf 和 printf
⽽且,C++的 cin 和 cout 更加易⽤。
·scanf 和 printf :格式化输出更精确直观,特别适合复杂格式的输⼊输出,⽐如:在要求指
定格式输出的时候, printf 函数就⽐ cout 更加⽅便和灵活。
cpp
#include <cstdio>
#include <iostream>
using namespace std;
int main()
{
float a = 3.50;
double d = 16.50;
cout << "cout: " <<a << " "<< d <<endl;
printf("printf: %f %lf\n", a, d);
return 0;
}
//cout: 3.5 16.5
//printf: 3.500000 16.500000
- cout 默认不会输出六位⼩数,⾃动忽略⼩数点后多余的 0 , printf 函数打印浮点数的时候,⼩数点默认打印6位。
- cout 在输出的时候不需要指定格式, printf 则需要明确的格式。
3.2 性能差异
3.2.1 案例演⽰
结论: scanf 和 printf 通常⽐ cin 和 cout 快。
原因: cin 和 cout 由于要考虑兼容C语⾔的输⼊和输出,封装实现的更加复杂,通常⽐ scanf 和 printf 稍慢,但这种差异在⼤多数应⽤场景中可以忽略不计。 但是在竞赛的题⽬中,尤其是当输⼊、输出数据量较⼤时,使⽤ cin 和 cout 完成输⼊输出,经常 会出现 Time Limit Exceeded 的情况。⽽ scanf 和 printf 就不存在类似的问题。下⾯给⼤ 家准备了两个案例。