【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() 函数。

注:文件也可以是标准输入输出流 stdinstdout

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



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。