【2024最新】C++读写优化超详细解析(cin优化+普通快读+fread)_输入输出优化_快读快写_算法竞赛
YLCHUP 2024-08-06 08:35:01 阅读 66
文章目录
一、scanf/printf二、cin/cout 优化三、endl 优化四、传统快读、快写关于 isdigit()快读快写
五、位运算优化快读、快写六、优化快读、快写 plusfread、fwrite 用法优化快读优化快写
End
在算法竞赛中,读入速度和输出速度一直是卡常的重要手段。也有不少人经历过被题目卡 cin 的情况。今天我给大家介绍一下算法竞赛中常用的读写方法及其优化。
声明:大部分情况下,只用读入优化就行。对于输出量大的题目再考虑使用输出优化。
一、scanf/printf
这两个函数来源于传统的 C,在 <code><stdio.h> 和 <cstdio>
(仅 C++)中。使用方法:scanf("%占位符",&var); printf("%占位符\n",var);
。这样比传统的 cin/cout 快,但由于书写复杂而很少被人采用。
scanf/printf 用法参考文章:详解c++中scanf和printf用法
二、cin/cout 优化
cin 和 cout是 C++ 的标准输入输出流,在 <iostream>
头文件中,使用方法也十分简单:cin>>var; cout<<var<<endl;
所以深受人们的喜爱。
在时间要求不高的题目中,用它们既省事,又可以让代码更美观。
缺点:cin/cout 在不加优化的情况下速度慢于 scanf 和 printf。
原因:C++ 为了和 C 保持同步、在混用 printf&scanf 和 cin&cout时的时候不发生混乱,将它的输入/输出流绑到了一起。
解决方案:可以手动关闭同步,从而提高 cin 和 cout 的效率。std::ios::sync_with_stdio(false);
同时,还可以通过std::cin.tie(nullptr);
来解除 cin 和 cout之间的绑定,进一步减轻 cin 的负担。
cin/cout 用法:C++ 基础的输入输出介绍:掌握cin与cout的奥秘
三、endl 优化
有一个被人忽略的“大杀手”:endl
。这一个语句和 \n
的效果相同,但更方便书写,因此被人们广泛采用。其实它的速度也特别慢。可能会导致某些题目超时。
原因:endl
输出时会清空缓冲区,这是为了让用户能及时看到输出,但会拖慢速度。并且由于它本身的功能定义,很难进行优化。
解决方案:打不过就不用呗 对于输出量大且需要换行的题目,使用 endl
有超时的风险,建议使用 cout<<'\n'
。(平常刷题也建议用 \n
,比较保险)
在实测中,C++14 的版本下,cin/cout 优化 + endl 优化的速度已经远快于 scanf/printf,建议在新评测机上使用上面两种方法优化。
#include<iostream>
using namespace std;
int a[105];
int main(){ -- -->
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
cout<<"a["<<i<<"]="<<a[i]<<"\n";
}
return 0;
}
四、传统快读、快写
原理:单个字符的输入输出速度要比读入数字快,因此我们以字符的形式先读入,然后处理计算转为数字,再用字符输出。
关于 isdigit()
在判断某个字符是不是整数时用的 isdigit()
函数,在不同编译器上的速度不一样。
在 这篇文章 中的测试结果:自己写比 VS 中的 isdigit() 快,但比 g++ 中的 isdigit() 慢。而且 g++ 中的 isdigit() 比 VS 中的 isdigit() 更快。
这篇文章 给我们展示了 Linux 内核中该函数王者级别的实现,大家可以学习。
自己写的话:
#define my_isdigit(c) ( (c)>='0'&&(c)<='9' ) // C语言
inline bool my_isdigit(char c){ return (c>='0'&&c<='9'); } // C++
快读
原理是先用单个字符读入所有零散的数字,再拼起来。拼数和判断字符见 这篇文章。
template<typename T>
inline void read(T &x){
T w=1; x=0;
char c=getchar();
while(!isdigit(c)){ if(c=='-'){ w=-1;} c=getchar();}
while(isdigit(c)){ x=x*10+(c-'0'); c=getchar();}
x=x*w;
}
快写
原理是把原来完整的数字拆成一个个字符输出。用递归的常数会略大,可以用模拟栈的方法时限。
递归代码:
template<typename T>
inline void write(T x){
if(x<0){ putchar('-'); x=-x;}
if(x>=10){ write(x/10);}
write(x%10+'0'); //+'0'是为了让数字类型的x%10变成字符输出
}
模拟栈代码:(不能用模板了,要自己指定类型)
inline void write(int x){
static int st[35] = { 0}; // int 最多32位
int top = 0; // 指向栈顶的指针
do{
st[top++]=x%10;
x/=10;
}while(x>0);
while(top>0){ putchar(st[--top]+'0');}
}
这里有一个细节,把 st 数组声明为 static
静态类型,可以在申请空间时只申请一次,同时降低常数。static
关键字详细用法见 这篇文章。
五、位运算优化快读、快写
原理:通过位运算加速拼数、取数及转换数字的过程。
实现如下:
template<typename T>
inline void read(T &x){
T w=1; x=0;
char c=getchar();
while(!isdigit(c)){ w|=(c=='-'); c=getchar();}
while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar();}
x=x*w;
}
template<typename T>
inline void write1(T x){
if(x<0){ putchar('-'); x=(~x)+1;}
if(x>=10){ write(x/10);}
write(x%10^48); //+'0'是为了让数字类型的x%10变成字符输出
}
inline void write2(int x){
static int st[35] = { 0}; // int 最多32位
int top = 0; // 指向栈顶的指针
do{
st[top++]=x%10;
x/=10;
}while(x>0);
while(top>0){ putchar(st[--top]^48);}
}
上面一些位运算优化的解释参见:这篇文章
六、优化快读、快写 plus
原理:使用 C 中的 fread()
和 fwrite()
函数,把数据当作字符串 直接从文件中读入/向文件中输出。加快 getchar()
函数。
注:文件也可以是标准输入输出流 stdin
、stdout
。
fread、fwrite 用法
fread 函数:从一个文件流中读取数据
函数原型如下:
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
-- buffer:指向数据块的指针,读取的数据存储在此
-- size:每个数据的大小,单位为Byte 例如:sizeof(int)就是4
-- count:要读入的数据个数
-- stream:文件指针,指向要读入的数据
fwrite 函数:将一块内存区域中的数据写入到本地文本
函数原型如下:
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
-- buffer:指向数据块的指针,待写入的数据存储在此
-- size:每个数据的大小,单位为Byte 例如:sizeof(int)就是4
-- count:要写入数据个数
-- stream:文件指针,指向待写入的数据
拓展:两个函数随着传入参数不同,返回值的意义也不同。
fread(buf,sizeof(buf),1,file)
:读取数据量正好是 sizeof(buf)
个 Byte 时,返回值为 1(为 count)。fread(buf,1,sizeof(buf),file)
:读取成功返回值为实际读入的数据个数(单位为Byte)。fwrite(buf,sizeof(buf),1,file)
:成功写入返回值为 1(为 count)。fwrite(buf,1,sizeof(buf),file)
:成功写入则返回实际写入的数据个数(单位为Byte)。
更具体的详细用法参见 这篇文章。
优化快读
char buf[1<<20],*p1,*p2;
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1<<20,stdin), p1 == p2) ? 0 : *p1++)
template <typename T>
inline void read(T &x){
T w=1; x=0;
char c=gc();
while(!isdigit(c)){ w|=(c=='-'); c=gc();}
while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=gc();}
x=x*w;
}
优化快写
const int bsiz = 1000000;
int sta[30];
char buf[1<<20], pbuf[1<<20], *p = pbuf, *s = buf, *t = buf;
#define put1(ch) (p - pbuf == bsiz ? fwrite(pbuf, 1, bsiz, stdout), p = pbuf, *p++ = ch : *p++ = ch)
inline void putint(int x){
register int top = 0;
if (x<0) put1('-'), x = -x;
do{
sta[top++]=x%10;
x/=10;
}while(x>0);
while (top) put1(sta[--top]^48);
}
// ...
int main(){
// ...
fwrite(pbuf, 1, p - pbuf, stdout); // 程序最后一定要加上这一句,让存储到输出流里的内容输出
return 0;
}
End
本文就到这里啦~ 有不完善的地方日后会加以完善的(咕了几篇文章 )
这里是 YLCHUP,拜拜ヾ(•ω•`)o
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。