【算法竞赛】C/C++ 的输入输出你真的玩会了吗?

【算法竞赛】C/C++ 的输入输出你真的玩会了吗?
在这里插入图片描述
🔭 个人主页:散峰而望

《C语言:从基础到进阶》《编程工具的下载和使用》《C语言刷题》《算法竞赛从入门到获奖》《人工智能AI学习》《AI Agent》

愿为出海月,不做归山云


🎬博主简介

在这里插入图片描述
请添加图片描述

文章目录


前言

在编程的世界中,输入输出(I/O)是与用户或外部系统交互的基础。C 和 C++ 提供了强大的 I/O 机制,从简单的 printf 和 scanf 到 C++ 的流式操作 cin 和 cout,看似简单,实则暗藏玄机。许多开发者在使用过程中可能会遇到缓冲区问题、格式控制陷阱、性能瓶颈,甚至安全性隐患。
本文将对 C/C++ 一些关于在输入输出的问题进行一一打击。

1. OJ(online judge)题目输入情况汇总

在竞赛的 OJ 题目中,一般关于输入场景总结为下面四类:

在这里插入图片描述

由于单独讲解可能体验不是那么深,所以接下来,我们就结合题目,给大家分别介绍。

1.1 单组测试用例

  1. 计算 (a+b)/c 的值
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a, b, c; cin >> a >> b >> c; cout <<(a + b)/ c << endl;return0;}
  1. 与 7 无关的数
在这里插入图片描述
思路
先输入 n,之后循环产生 1~n 的数字,接着找出与 7 无关的正整数,最后求平方和。
那么如何产生与 7 无关的正整数呢?
我们不如先找与 7 相关的正整数,然后取反就能得到与 7 无关的正整数。
又由题目知道 n < 100,所以只需在 100 内的数字进行判断。
即与 7 相关的条件有:被 7 整除,%10 == 7(个位),/10 == 7(十位)

参考代码:

#include<iostream>usingnamespace std;intmain(){int n; 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;return0;}

1.2 多组测试用例

1.2.1 测试数据组数已知

  1. 多组输入a+b II
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int n;//表示数据组数 cin >> n;int a, b;//每行输入的a,b值 while(n--){ cin >> a >> b; cout << a + b << endl;}return0;}
  1. 斐波那契数列
斐波那契数列:1 1 2 3 5 8 13 21 34 55 …
特点:前两个数的和是第三个数
在这里插入图片描述
在这里插入图片描述

斐波那契有多种做题办法

参考代码:

  • 正常的循环嵌套:
#include<iostream>usingnamespace std;intmain(){int n, a; cin >> n;while(n--){ cin >> a;int x =1;int y =1;int z =1;while(a >2){ z = x + y; x = y; y = z; a--;} cout << z << endl;}}
  • 由题目可知,题目把所求范围限制在 1~30 之间,所以提前算出前 30 个斐波那契数,存储下来输入 Q 后,直接拿取第 a 个斐波那契数:
#include<iostream>usingnamespace std;intmain(){int n, a, i;int ret[35]={0,1,1};for(i =3; i <30; i++){ ret[i]= ret[i -1]+ ret[i -2];} cin >> n;while(n--){ cin >> a; cout << ret[a]<< endl;}return0;}
  • 还有一种方法是递归,后面会进行讲解
  1. 制糊串
在这里插入图片描述
在这里插入图片描述
思路:输入 s,t,qq 次询问
while(q–)
{ }
处理一组数据
输入 l1,r1,l2,r2取出子串 s1,t1比较子串的大小,并输出
字符串的比较 - 支持关系运算的比较

参考代码:

#include<iostream>#include<string>usingnamespace std;intmain(){ string s, t; cin >> s >> t;int q; cin >> q;int l1, r1, l2, r2;while(q--)// 这种写法是常?的处理 q 次询问的方式 { cin >> l1 >> r1 >> l2 >> r2; string s1 = s.substr(l1 -1, r1 - l1 +1);// 注意这道题的字符串是从 1 开始计数的  string t1 = t.substr(l2 -1, r2 - l2 +1);if(s1 < t1){ cout <<"yifusuyi"<< endl;}elseif(s1 > t1){ cout <<"erfusuer"<< endl;}else{ cout <<"ovo"<< endl;}}return0;}
编程技巧:
题目中说"有 q 次询问",意思是程序要处理q组测试数据,(也就是对应 q 次循环),我们要针对每次询问,给出一个结果。
其实就是之前的单组测试变成了 q 组测试,在之前的代码上套一层 while 循环即可。当有 q 次询问的时候,while(q–) 是非常方便的方式。然后就按照单组输入的方式处理每组输入的数据就好了。

1.2.2 测试数据组未知

  1. 多组输入a+b
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a, b;while(cin >> a >> b){ cout << a + b << endl;}return0;}
📌cin >> a; 会返回一个流对象的引用,即 cin 本身。在 C++ 中,流对象 cin 可以被用作布尔值来检查流的状态。如果流的状态良好(即没有发生错误),流对象的布尔值为 true。如果发生错误(如遇到输入结束符或类型不匹配),布尔值为 false。在 while (cin >> a >> b) 语句中,循环的条件部分检查 cin 流的状态。如果流成功读取到 2 个值,cin >> a >> b 返回的流对象 cin 将被转换为 true,循环将继续。如果读取失败(例如遇到输入结束符或无法读取到2个值),cin >> a >> b 返回的流对象 cin 将被转换为 false,循环将停止。
  1. 数字三角形

参考代码:

#include<iostream>usingnamespace std;intmain(){int n;while(cin >> n){for(int i =1; i <= n; i++){for(int j =1; j <= i; j++){ cout << j <<" ";} cout << endl;}}}
  1. 定位查找
在这里插入图片描述
思路:先输入 n,指明要存储多少个数
2.再输入 m,并和输入的数组中的数据进行比较查找
3.只要查找的一出现就输出并跳出循环,如果没有就输出 No

参考代码:

#include<iostream>usingnamespace std;constint N =25;int arr[N];intmain(){int n, m;while(cin >> n){for(int i =0; i < n; i++){ cin >> arr[i];} cin >> m;int i =0;for(i =0; i < n; i++){if(m == arr[i]){ cout << i << endl;break;}}if(i == n){ cout <<"No"<< endl;}}return0;}

1.2.3 特殊值结束测试数据

  1. 字符统计
在这里插入图片描述

参考代码:

//代码1#include<iostream>usingnamespace std;intmain(){int ch =0;int Letters =0;int Digits =0;int Others =0;while((ch =getchar())!='?'){if((ch >='a'&& ch <='z')||(ch >='A'&& ch <='Z')){ Letters++;}elseif(ch >='0'&& ch <='9'){ Digits++;}else{ Others++;}} cout <<"Letters="<< Letters << endl; cout <<"Digits="<< Digits << endl; cout <<"Others="<< Others << endl;return0;}
//代码2#include<iostream>usingnamespace std;intmain(){ string s;int Letters =0;int Digits =0;int Others =0;getline(cin, s); s.pop_back();//去掉? for(auto ch : s){if((ch >='a'&& ch <='z')||(ch >='A'&& ch <='Z')) Letters++;elseif(ch >='0'&& ch <='9') Digits++;else Others++;} cout <<"Letters="<< Letters << endl; cout <<"Digits="<< Digits << endl; cout <<"Others="<< Others << endl;return0;}

这里判断大小写字母数字可以用一种简单的方法来判断,运用函数:

if(islower(ch)||isupper(ch)) Letters++;elseif(isdigit(ch)) Digits++;else Others++;

当然判断字母还能再简化一下

if(isalpha(ch)) Letters++;elseif(ch >='0'&& ch <='9') Digits++;else Others++;

可以拓展了解一下 :isalphaisdigit

  1. 多组数据a+b III
在这里插入图片描述

参考代码:

#include<iostream>usingnamespace std;intmain(){int a =0, b =0;while(cin >> a >> b, a && b){ cout << a + b << endl;}return0;}
这里的逗号表达式简单介绍一下:从左往右依次计算整个表达式的结果是最后一个表达式的结果

OK 了,有关OJ(online judge)题目输入情况的所有情况都讲完了,希望各位友友们在做题中能够有着清晰的判断。

2. 输入时特殊技巧

2.1 含空格字符串的特殊处理方式

根据我们现在掌握的知识,含空格的字符串,如要读取有 fgets、scanf、getchar、getline 四种方式解决,但是有时候,根据题目的情况,不一定非要完整的读取这个带空格的字符串,而是将字符串中空格隔开的每一个字符串,当做一个单词处理更方便,也避免了读取带空格字符串的各种问题。

练习:统计数字字符个数

在这里插入图片描述

这里我们有两种做法:

整个字符串一次性读取,然后处理字符串,使用 getline使用逐个单词的方式处理 cin
  1. 法一:读取整个带空格的字符串分析
#include<iostream>#include<string>usingnamespace std;intmain(){ string s;getline(cin, s);int ret =0;for(auto ch : s){if(ch >='0'&& ch <='9'){ ret++;}} cout << ret << endl;return0;}
  1. 法二:按照多个单词分析
#include<iostream>#include<string>usingnamespace std;intmain(){ string s;int cnt =0;while(cin >> s){for(auto c : s){if(c >='0'&& c <='9') cnt++;}} cout << cnt << endl;return0;}

同时由上面的部分介绍,我们可以把

if(ch >='0'&& ch <='9')

修改为

if(isdigit(ch))

2.2 数字的特殊处理方式

当我们程序运行的时候,在控制台输入123 的时候,这时的 123 是三个字符,123 是一个字符序列,程序会根据代码中的数据类型,可能将123解析成整型,也可能将 123 解析成字符串。

比如:

int num =0; cin >> num;//输入123, 就被解析成整数  string s; cin >> s;//输入123, 就被解析成字符串

这里的解析的方式,主要是依赖编译器对变量类型的识别,根据类型再将读取字符串数据转化成对应类型的数据。

我们在写代码的时候,应该根据实际的情况,来决定如何处理输入的内容。

练习:小乐乐改数字

在这里插入图片描述
在这里插入图片描述
  1. 法一:当做整数读取
输入一个整数 n获取 n 的每一位
% 10,判断是奇数(1)还是偶数(2)
/ 10,求后面的数字
3.输出转换后的整数
#include<iostream>#include<cmath>usingnamespace std;intmain(){int n;int i =0, ret =0; cin >> n;while(n){if(n %10%2==1){ ret +=pow(10, i);} n /=10; i++;} cout << ret << endl;return0;}
  1. 法二:当做字符串处理
由于读取的是字符,所以 1,2,3 不能当作字符来判断,而是用 ASCII 判断
又 ‘0’ = 48,‘1’ = 49,‘2’ = 50,‘3’ = 51 … 发现 ASCII 为偶数时对应的正好为偶数,奇数时正好为奇数,故可以 %2 判断。
#include<iostream>#include<string>usingnamespace std;intmain(){ string s; cin >> s;for(int i =0; i < s.size(); i++)// 数字字符与对应的数的奇偶一致 {if(s[i]%2){ s[i]='1';}else{ s[i]='0';}} cout <<stoi(s)<< endl;// 转换成数字输出 return0;}
这里说一下代码的一些函数pow(a, b) 求 a 的 b 次方stoi 是 C++11 标准引入的字符串转换函数,用于将字符串转换为整数

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 更加方便和灵活。
#include<cstdio>#include<iostream>usingnamespace std;intmain(){float a =3.50;double d =16.50; cout <<"cout: "<<a <<" "<< d <<endl;printf("printf: %f %lf\n", a, d);return0;}

输入结果如下:

在这里插入图片描述
区别: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 就不存在类似的问题。下面给大家准备了两个案例。

案例一:数字游戏

代码展示:

  1. 使用 cin 和 cout:
#include<iostream>usingnamespace std;int t, x;intmain(){ cin >> t;while(t--){ cin >> x;int ret =0;while(x){int count =0, high =0;int tmp = x;while(tmp){//计算最右边的1代表的值 int low = tmp &-tmp;//如果low中剩余的1就是最后一个1 //就是最左边的1 if(tmp == low){ high = low;}//去掉最右边的1  tmp -= low; count++;}if(count %2==0){ x -= high;}else{ x ^=1;} ret++;} cout << ret << endl;}return0;}

运行结果展示:

在这里插入图片描述
  1. 使用 scanf 和printf:
#include<iostream>usingnamespace std;int t, x;intmain(){scanf("%d",&t);while(t--){scanf("%d",&x);int ret =0;while(x){int count =0, high =0;int tmp = x;while(tmp){//计算最右边的1代表的值 int low = tmp &-tmp;//如果low中剩余的1就是最后一个1 //就是最左边的1 if(tmp == low){ high = low;}//去掉最右边的1  tmp -= low; count++;}if(count %2==0){ x -= high;}else{ x ^=1;} ret++;}printf("%d\n", ret);}return0;}

运行结果展示:

在这里插入图片描述

案例 2:求第 k 小的数

  1. 使用 cin 和 cout:
#include<iostream>#include<cstdio>#include<algorithm>usingnamespace std;constint N =5000010;int arr[N];intmain(){int n, k; cin >> n >> k;for(int i =0; i < n; i++){ cin >> arr[i];}sort(arr, arr + n); cout << arr[k]<< endl;return0;}

运行结果展示:

在这里插入图片描述
  1. 使用 scanf 和printf:
#include<iostream>#include<cstdio>#include<algorithm>usingnamespace std;constint N =5000010;int arr[N];intmain(){int n, k; cin >> n >> k;for(int i =0; i < n; i++){scanf("%d",&arr[i]);}sort(arr, arr + n); cout << arr[k]<< endl;return0;}

运行结果展示:

在这里插入图片描述

我们可以看到这两个案例中,输入的数据量都比较大,在输入数据的时候如果使用cin,都会出现超时的问题,但是换成是 scanf 的方式就能正确的通过。这就是因为两者性能上的差异导致的。那为什么 cin/cout 的性能要低于 scanf/printf 呢?有什么办法能优化吗?

后面会单独对这个问题进行讲解。

总结一下其实就是 2 个点:

  1. C++ 中为了支持混合使用 cin/cout 和 scanf/printf,C++ 标准库默认会将 cin、cout
    等 C++ 流对象与 stdin、stdout 等 C 标准库的流对象同步在一起。这种同步操作意味着每次使用 cin或cout 时,都会自动刷新 C 标准库的缓冲区,以保 C++ 和 C 的 I/O 是一致的。这就导致了性能的下降。
  2. 在默认情况下,cin 和 cout 之间存在一种绑定关系。这种绑定意味着,每当从 cin 读取数据时,任何之前通过 cout 输出的内容都会被强制刷新到屏幕上。这种绑定也可能导致性能问题,特别是在需要频繁读取大量数据的情况下。

3.2.2 优化方案和演示

通过下面这个案例我们看一下 scanf 和 cin 的差异,然后再看看如何优化。

#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){int i, x;//freopen是将stdin重定向到文件 //意思是scanf可以文件中读取数据 freopen("data.txt","r",stdin); clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){scanf("%d",&x);} t2 =clock(); cout <<"Runtime of scanf: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述
#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){//freopen是将stdin重定向到文件 //意思是cin可以文件中读取数据 freopen("data.txt","r",stdin);int i, x; clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){ cin >> x;} t2 =clock(); cout <<"Runtime of cin: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述

如果对 cin 进行优化呢?

#include<iostream>#include<ctime>#include<cstdio>usingnamespace std;constint num =10000000;intmain(){ ios::sync_with_stdio(false);//取消给C语言输入输出缓冲区的同步  cin.tie(0);//取消了cin和cout的绑定 freopen("data.txt","r",stdin);int i, x; clock_t t1, t2; t1 =clock();for(i =0; i < num; i++){ cin >> x;} t2 =clock(); cout <<"Runtime of cin: "<< t2 - t1 <<" ms"<< endl;return0;}

运行演示结果:

在这里插入图片描述

所以未来我们在使用 scanf / printf 和 cin / cout 抉择的时候,如果要追求性能那就使用 scanf / printf,或者优化版的 cin / cout,如果不追求性能,直接使用 cin / cout 就行。

如果输入的数据量比较小(10^6 以内)的话,用 cin 和 cout 或 scanf 和 printf 都行;但是输入的数据量比较大(10^9 左右)的话,更推荐使用 scanf 和 printf,避免因为输入输出的开销,导致代码超时;在大多数场景下 printf / scanf 和 cin / cout 的使用根据个人习惯进行选择
即可。

这里我们讨论了 scanf/printf 和 cin/cout 性能的差异,我们知道 scanf/printf 是更快
的,其实有一些极端情况下,输入输出的规模非常大的时候,scanf 和 printf 也不能满足的时候,会使用快速读写的方式,这个后期会讲。


结语

掌握输入输出的底层机制和陷阱,才能写出健壮高效的代码。从缓冲策略到格式控制,每个细节都可能成为调试时的关键。

希望这篇文章能给各位朋友对 C/C++ 的输入输出有着不一样的理解。

同时愿诸君能一起共渡重重浪,终见缛彩遥分地,繁光远缀天



Read more

三个简单Python策略,让你的交易系统更聪明

三个简单Python策略,让你的交易系统更聪明

作者:老余捞鱼 原创不易,转载请标明出处及原作者。 写在前面的话:很多人以为量化就是复杂的数学模型和高深的算法,其实不然(难)。这篇文章会带你用Python实现三种用的最多的经典策略:均线交叉、均值回归和趋势跟随,并告诉你在什么市场环境下该用哪一个。不用怕,跟着我,一步步来,你也能推开量化世界的大门。 一、为什么简单的策略往往更有效? 在量化交易领域摸爬滚打这么多年,我发现一个有趣的现象:许多散户投资者总是追求复杂的算法和高深的模型,觉得越复杂越厉害。但事实上,华尔街那些顶级对冲基金的核心策略,往往简单得让人意外。 复杂并不等于有效。就像科学研究和市场营销一样,在交易中,简单、经过充分测试的策略,往往能在长期内跑赢那些看起来很炫酷的复杂模型。关键不在于策略有多复杂,而在于你能不能严格执行,能不能做好风险管理,能不能在正确的时间用对正确的方法。 💡 老余的经验之谈: 我见过太多人在回测时拿到漂亮的数据,但一到实盘就亏得一塌糊涂。原因就是策略太复杂,执行起来变形走样。记住一句话:能坚持执行的简单策略,永远强过只能看不能用的复杂模型。

By Ne0inhk
Python在AI虚拟教学视频开发中的核心技术与前景展望

Python在AI虚拟教学视频开发中的核心技术与前景展望

Python在AI虚拟教学视频开发中的核心技术与前景展望 一、引言:AI虚拟教学的技术革新 随着教育数字化转型加速,AI虚拟教学视频凭借个性化、沉浸式体验成为教育科技的新风口。Python以其强大的多模态处理能力、丰富的开源生态和跨领域兼容性,成为构建智能教学视频系统的首选技术栈。本文结合前沿研究与实战经验,解析Python在AI虚拟教学视频开发中的核心技术框架与典型应用场景。 二、核心技术框架与关键工具库 (一)计算机视觉:构建交互感知系统 Mediapipe:高精度姿态检测 Google开源的Mediapipe提供跨平台的人脸/手势/身体关键点检测,支持实时追踪教师演示动作并映射到虚拟人,提升交互真实感。 import mediapipe as mp mp_drawing = mp.solutions.drawing_utils mp_face_mesh = mp.solutions.face_mesh with mp_face_mesh.FaceMesh(max_num_faces=1)

By Ne0inhk
Prompt、Agent、Function Call、Skill、MCP,傻傻分不清楚?

Prompt、Agent、Function Call、Skill、MCP,傻傻分不清楚?

前言 最近AI越来越火了。 我发现里面有很多概念有些小伙伴有点分不清楚,比如:Prompt、Agent、Function Call、Skill、MCP等。 今天这篇文章专门跟大家一起聊聊这个话题,希望对你会有所帮助。 更多项目实战在项目实战网:Java突击队 核心概念关系图 先上干货,这张图让你从整体上理解这五个概念是如何分层递进的: 一句话概括: * Prompt 是你跟AI说的“人话” * Function Call 让AI能“动手干活” * Agent 让AI会“思考规划” * Skill 是AI的“职业技能证书” * MCP 是AI世界的“USB接口” 下面我们一层一层拆开揉碎了讲,每层都有Java代码示例。 第一层:Prompt——和AI对话的“普通话” 1.1 什么是Prompt? Prompt(提示词) 就是你输入给AI的文本指令。 它就像你去餐厅点菜时说的“来一份宫保鸡丁”,AI就是那个服务员,听懂你的话然后给你上菜。

By Ne0inhk
计算机毕业设计:Python个性化音乐推荐系统 Django+MySQL + 双协同过滤算法实现精准推荐 人工智能 大数据 (建议收藏)✅

计算机毕业设计:Python个性化音乐推荐系统 Django+MySQL + 双协同过滤算法实现精准推荐 人工智能 大数据 (建议收藏)✅

博主介绍:✌全网粉丝50W+,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,选择我们,就是选择放心、选择安心毕业✌ > 🍅想要获取完整文章或者源码,或者代做,拉到文章底部即可与我联系了。🍅 点击查看作者主页,了解更多项目! 🍅感兴趣的可以先收藏起来,点赞、关注不迷路,大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助同学们顺利毕业 。🍅 1、毕业设计:2026年计算机专业毕业设计选题汇总(建议收藏)✅ 2、最全计算机大数据专业毕业设计选题大全(建议收藏)✅ 1、项目介绍 技术栈 Python语言、Django框架、MySQL数据库、双协同过滤推荐算法、css + js + HTML 功能模块 * 用户信息管理 * 音乐展示 * 音乐下载 * 音乐收藏 * 音乐评分 * 音乐评论 * 在线听歌 * 音乐推荐 * 后台数据管理

By Ne0inhk