玄学优化:从入门到精通

chen_zhe 沙雕 2020-08-18 17:59:46 2020-08-19 13:02:05 0

最近颓得要命,于是写篇玄学论文扯淡聊以自慰

1、IO优化

读入优化

cin

慢得可以,比赛用这个其实就是自寻死路

scanf

比较慢,比赛多用的还是下面两种读入方法

getchar读入优化

inline int read()
{
	char ch;
	int x = 0, f = 1;
	while ((ch = getchar()) < 48 || ch > 57)
	if (c == '-') f = -1;
	while (ch >= 48 && ch >= 57)
	x = (x << 1) + (x << 3) + ch - 48, ch = getchar();
	return x * f;
}

一般getchar足够应付不刁钻的题目,可是最近NOIP真的卡常卡得毒瘤至极,这时我们帅气的fread就闪亮登场了:

char ibuf[500 << 20] , * s;
struct io
{
    io()
    {
        fread(s = ibuf, 1, 500 << 20, stdin);
    }
    inline int read()
    {
        register int u = 0;
        while (* s < 48  || * s > 57) s ++;
        while (* s >= 48 && * s <= 57)
            u = u * 10 + * s ++ - 48;
        return u;
    }
} ip;

调用时介样调用:x = ip.read()

如果在本地运行并且使用了fread,那么请在输入结束后按回车键,再按Ctrl+z,再按回车键结束输入

看不懂没关系,能默写就行

简单地解释一下代码:

io结构体中有一个同名函数,创建结构体时将默认调用它;

*s是一个指针,它始终指向ibuf数组的一个位置;

fread函数第一个参数是读取地数据存放的场所(原型里是一个指针,但不用管这些),第二个参数是每次读取的字节数,我们想每次读入一个字符存到ibuf里;

那么一个字符是一个字节大小的,所以在这里一般都写1;

那么fread函数每读入一个字节后,传入的指针(这里是ibuf,并“顺便”把s指针赋值为ibuf数组的首地址)都会后移一位;

第三个是最多读取的字节,把它设置为一个很大的值就行了,不用管其他的;

第四个参数是你要从哪里读入数据,在这里是标准输入流(stdin),默认是键盘,也可以重定向(freopen)把标准输入流改为某个文件;

看不懂很正常,没关系,会默写就OK

scanf只比cin快一点,getchar比scanf快很多,fread比getchar快很多

输出优化

cout

强烈不推荐使用

printf

可以使用

putchar优化

推荐使用,代码就不给出了,比较好写

puts

能用puts就用puts,要输出数字用putchar或者printf或者下面介绍的fwrite即可

fwrite

在这里不解释代码,会指针都看得懂,fwrite函数格式和fread差不多的。

inline void print(register int u)
{
	char obuf[105], *s = obuf;
	register int x = 1;
	while (x <= u) x *= 10;
	x /= 10;
	while (x) *s ++ = u / x + 48, u -= u / x * x, x /= 10;
	fwrite(obuf, 1, s - obuf, stdout);
}

ps:虽然输出大量数据时fwrite速度远超其他方法,但输出量较少时比puts稍慢。在有些OJ上可能fwrite还不如putchar,但在OI中fwrite绝对是最快的

输入输出量越大,各种方式差距的比例也越大。开始可能getchar和fread速度完全一样,可是数据大了(比如一亿个)那fread速度就是getchar的几十倍了……

不过一般输出量很少,所以输出优化意义不大,但是也有输出量特别多的

OI中最快的输出写法:

输出单个字符putchar,字符串puts,数字fwrite输出

2、inline,register,define超级玄学优化

register一般没什么用,格式:register 类型名 变量名;

在声明函数前加个inline,可减少跳转和传参的代价,比register有用点(详见上面的fread的read函数)

在dfs或其他需要多次递归调用函数的时候,inline不可不加,上次我某道题在dfs时不加inline70分,加了就80分了……

define就是一个替换,用它来代替函数可以去掉跳转和传参的代价,但是注意define别爆炸,一般还是用inline稳妥一些

3、数组优化

把数组开小,把int的数组能改成char就改成char,会提高程序效率。

在数组大小达到一百万以上时,访问速度会比较的慢

4、去STL

手写,因为STL常数特别高。。。

STL的除了set其余的容器和算法大家应该都会手写吧……

5、去指针优化

比如说一个最短路,明明可以邻接链表和vector存,你非要指针形式……

常数*100000000

6、卡时优化

比如for循环只for几次,比如dfs递归到几次或者几层就直接return,比如SPFA某个点入队了100次就坚信这个图有负环……

总之,这个技巧使用乱搞恰当的话,可以让你本来就能AC的点继续AC,TLE的点多AC几个点

技巧原理:宁愿WA也不愿意TLE

例:

有一次一道最短路有负权还卡SPFA,加了SLF还超时,全班统一做法:某个点进队200次直接判负环,inline + register,deque换成手写队列

就AC了……

各种玄学的比较

效果方面

1、卡时优化

2、去指针优化

3、去STL优化

4、数组开小优化

5、读入优化

6、inline,register,define

风险方面

1、卡时优化

2、去STL优化

3、数组开小优化

4、读入优化

5、去指针优化

6、inline,register,define

后三个差不多没有任何风险

{{ vote && vote.total.up }}

共 12 条回复

root 站长

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Duke

%%%