关于卡常那些事

关于卡常那些事

今天下午遇到了一道题,原oj能过,但学校oj就是TLE,虽然后来发现是算法有点慢,但是这一下午卡常的经历还是学到了不少东西,于是就来总结一下卡常的常用方法。

卡常操作Lv.1——快读快写

但凡学过OI的人要开始卡常第一时间想到的都是快读快写,不细讲了,板子背住就行(狗头

inline int Read()     
{
    int res=0,ch,flag=0;
    if((ch=getchar())=='-')
        flag=1;
    else if(ch>='0'&&ch<='9')
        res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+ch-'0';
    return flag?-res:res;
}
inline void Write(int a)    
{
    if(a>9)
        Write(a/10);
    putchar(a%10+'0');
}

使用时:

int n=Read();
Write(n);

卡常操作Lv.2——inline优化

inline可以看成是函数的宏展开
在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数,栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
inline的使用是有所限制的,inline只适合涵数体内代码简单的涵数使用,
(1) 不能包含复杂的结构控制语句例如while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。
(2) 而所有(除了最平凡,几乎什么也没做)的虚拟函数,都追阻止inlining的进行。
这应该不会引起太多的惊讶,因为virtual意味着”等待,直到执行时期再确定应该调用哪一个函数“,
而inline却意味着”在编译阶段,将调用动作以被调用函数的主体取代之“。
如果编译器做决定时,尚不知道该调用哪一个函数,你就很难责成他们做出一个inline函数。

卡常操作Lv.3——register优化

register其实就是寄存器的意思。
平时定义临时变量的时候用register可以加快速度。
就比如说大家都喜欢写for(int i=1;i<=n;i++)
其实这样写更快:for(register int i=1;i<=n;i++)

卡常操作Lv.4——短整型(字符型)卡常

众所周知,一个 int 类型的变量占4个字节,一个short类型的变量只占两个字节。理论上来说,short类型之间的运算速度是int类型之间的运算速度的一半,换句话说,如果这一道题变量开short能过的话,那么你可以选择开short,当然,char类型也是一个不错的选择(如果存得下的话)
另外提醒一句,我们是为了提高分数而卡常,千万不要因为开成了short而丢分。

卡常操作Lv.5——压位

如果考试的时候给的序列中的数比较小,或者是一个01序列,那么可以考虑把多个数压到一个数位上。记得提前预处理一下,会快很多哟。

卡常操作Lv.6——循环展开

这个比较神奇。

比如说一个程序,原本时间复杂度是 O(n^ 2) ,但是如果你用一些高科技(如循环展开)也许能够跑完3*10^4 的数据哦。
比如说
把这样的码

for(int i=1;i<=n;i++)
{
	a[i]++;
	b[i]++;
}

写成这样

for(int i=1;i<=n;i++) a[i]++;
for(int i=1;i<=n;i++) b[i]++

卡常操作Lv.7——下标连续访问

#include<bits/stdc++.h>
using namespace std;
int a[100000010];
int main()
{
	int n=1e8;
	for(int i=n;i;i--) a[i]++;
}

#include<bits/stdc++.h>
using namespace std;
int a[100000010];
int main()
{
	int n=1e8;
	for(int i=1;i<=n;i++) a[i]++;
}

这两份代码时间有差别,因为第二份的下标是从小到大连续访问的,会更快些,这东西如果运用到矩阵乘法内,可以让矩阵乘法原地起飞。

卡常操作Lv.8——少用algorithm库里的函数

比较慢……

inline int Min(int a,int b) {return a<b?a:b; }
inline void swap(int& a,int& b) 
{
	a^=b;
	b^=a;
	a^=b;
}
inline void equal_min(int& a,int b) {if(a>b)a=b;}

卡常操作Lv.9——吸氧大法(俗称火车头优化)NOIP慎用!!!

#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#include <immintrin.h>
#pragma GCC optimize(2)
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
%:pragma GCC optimize("-fgcse")
%:pragma GCC optimize("-fgcse-lm")
%:pragma GCC optimize("-fipa-sra")
%:pragma GCC optimize("-ftree-pre")
%:pragma GCC optimize("-ftree-vrp")
%:pragma GCC optimize("-fpeephole2")
%:pragma GCC optimize("-ffast-math")
%:pragma GCC optimize("-fsched-spec")
%:pragma GCC optimize("unroll-loops")
%:pragma GCC optimize("-falign-jumps")
%:pragma GCC optimize("-falign-loops")
%:pragma GCC optimize("-falign-labels")
%:pragma GCC optimize("-fdevirtualize")
%:pragma GCC optimize("-fcaller-saves")
%:pragma GCC optimize("-fcrossjumping")
%:pragma GCC optimize("-fthread-jumps")
%:pragma GCC optimize("-funroll-loops")
%:pragma GCC optimize("-fwhole-program")
%:pragma GCC optimize("-freorder-blocks")
%:pragma GCC optimize("-fschedule-insns")
%:pragma GCC optimize("inline-functions")
%:pragma GCC optimize("-ftree-tail-merge")
%:pragma GCC optimize("-fschedule-insns2")
%:pragma GCC optimize("-fstrict-aliasing")
%:pragma GCC optimize("-fstrict-overflow")
%:pragma GCC optimize("-falign-functions")
%:pragma GCC optimize("-fcse-skip-blocks")
%:pragma GCC optimize("-fcse-follow-jumps")
%:pragma GCC optimize("-fsched-interblock")
%:pragma GCC optimize("-fpartial-inlining")
%:pragma GCC optimize("no-stack-protector")
%:pragma GCC optimize("-freorder-functions")
%:pragma GCC optimize("-findirect-inlining")
%:pragma GCC optimize("-fhoist-adjacent-loads")
%:pragma GCC optimize("-frerun-cse-after-loop")
%:pragma GCC optimize("inline-small-functions")
%:pragma GCC optimize("-finline-small-functions")
%:pragma GCC optimize("-ftree-switch-conversion")
%:pragma GCC optimize("-foptimize-sibling-calls")
%:pragma GCC optimize("-fexpensive-optimizations")
%:pragma GCC optimize("-funsafe-loop-optimizations")
%:pragma GCC optimize("inline-functions-called-once")
%:pragma GCC optimize("-fdelete-null-pointer-checks")

加上这么一段就能让你的程序快到飞起

卡常操作Lv.10——clock()大法

clock()函数可以返回当前的运行时间,也就是说在进行循环或者爆搜的时候超时,时间限制为 1 s 我们就可以判断一下当前是否超时,如果快要超时了就立刻break出来,避免TLE
具体用法:

#include<bits/stdc++.h>
using namespace std;
int start=clock();
int main()
{
    cout<<CLOCKS_PER_SEC<<endl;
    for(int i=0;;++i)
    {
        if(i%100000==0&&clock()-start>=CLOCKS_PER_SEC * 0.9)
        {
            cout<<i<<endl;
            cout<<clock()-start<<endl;
            exit(0);
        }
    }
    return 0;
}

需要注意两点:

  • clock()函数本身运行比较慢,所以要尽量少用,少判断,所以我在代码的判断if一行加上了i % 100000000 == 0
  • clock()在不同的系统表示的时间单位不同,在liuns上是1000000为1s,而windows中经过测试为1000为1s

然后玩这个可以测试你的电脑的性能

另:一些琐碎的知识点

  1. ++i快于i++
  2. int x(5) 快于int x = 5
  3. 尽力减少除法运算
  4. 善用#define

最后:卡常只是在迫不得已的情况下应对某些lj评测和实在调不出题的时候用的,平时做题考试遇到TLE还是优先优化算法,少想歪门邪道的好!

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值