zoukankan      html  css  js  c++  java
  • OI比赛常数优化

    这是一篇玄学文章

    一、编译优化

    1 #pragma GCC optimize("O3")
    2 #pragma G++ optimize("O3")
    预处理开O3优化

    比赛时除非遇到常数很大可能会卡的暴力题否则一定不要用!玩火自焚!

    二、I/O优化

    核心:利用getchar()和putchar()这两个底层函数和位运算加速

    输入优化(超逼格写法)

     1 #include<cstdio>
     2 #include<cctype>
     3 using namespace std;
     4 int read()
     5 {
     6     int x=0,f=0;
     7     char c=getchar();
     8     while(!isdigit(c))
     9     {
    10         f|=c=='-';
    11         c=getchar();
    12     }
    13     while(isdigit(c))
    14     {
    15         x=(x<<3)+(x<<1)+(c^48);
    16         c=getchar();
    17     }
    18     return f?-x:x;
    19 }
    输入优化

    这里为什么可以用c^48代替c-48('0')呢?因为48的二进制是110000,后4位均为0,48~57('9')近在后四位后变化,所以^48可以用来代替-48且更快

    听说用fread更快但只能写文件,这里就不写代码了,通常比赛时使用上面的优化即可,有兴趣的读者可以查阅资料了解一下

    输出优化

    每次使用putchar()输出字符可加速输出

    实测递归+putchar()输出整数的速度比printf()快6倍!

    但不要写递归版……递归版的速度还慢于printf

    放上代码

    #include<cstdio>
    #include<cctype>
    using namespace std;
    #define re register int
    int stk[111],tt;
    void print(int x)
    {
        if(x==0)
            putchar('0');
        else
        {
            if(x<0)
                putchar('-'),x=-x;
            tt=0;
            while(x)
            {
                stk[++tt]=x%10;
                x/=10;
            }
            for(re i=tt;i;i--)
                putchar(stk[i]|48);
        }
    }
    输出优化

    格式控制直接手打putchar()即可

    stack[i]|48的正确性由输入优化的解释和或运算的定义显然可知

    三、寄存器变量

    需要大量运算的变量申请为寄存器变量,即register+类型名

    通常将循环变量设为寄存器变量

    另外将register int设为字符串常量可以简化代码,详见下面代码

    1 #define re register int
    2 int main()
    3 {
    4     for(re i=1;i<=999999;i++)
    5     return 0;
    6 }
    申请寄存器变量

    不要滥用register,一方面少量变量修改优化效果不明显,另一方面寄存器放不下变量时就会自动把变量放到内存里……

    四、取模优化

    适用于只有加减运算的题目

    int inc(int x,int v,int mod){x+=v;return x>=mod?x-mod:x;}//代替取模+
    int dec(int x,int v,int mod){x-=v;return x<0?x+mod:x;}//代替取模-
    取模优化

    五、memset,memcpy以及memmove

    memset(数组名+第一个操作数下标,0/-1,操作大小*类型所占字节数)

    memcpy(目标数组名+该数组第一个操作数下标,被copy数组名+该数组第一个操作数下标,操作大小*类型所占字节数)

    memmove(目标数组名+该数组第一个操作数下标,被清空数组名+该数组第一个操作数下标,操作大小*类型所占字节数)

    实例:

    1 //int是4个字节所以用<<2
    2 memset(a+l,0,r-l+1<<2);
    3 memcpy(a+l,b+l,r-l+1<<2);
    4 memmove(a+l,b+l,r-l+1<<2);
    memset,memcpy和remmove实例

    获取类型所占字节数方法:sizeof(类型名)

    六、正常的优化

    思路:减少运算及比较次数&用位运算代替四则运算

    七、程序框架

    将register int和long long缩写可以节省时间……用define就行

    另外多设一个INF

    #include<cstdio>
    #include<cctype>
    using namespace std;
    #define re register int
    #define ll long long
    const int INF=0x3f3f3f3f;
    int stk[111],tt;
    void print(int x)
    {
        if(x==0)
            putchar('0');
        else
        {
            if(x<0)
                putchar('-'),x=-x;
            tt=0;
            while(x)
            {
                stk[++tt]=x%10;
                x/=10;
            }
            for(re i=tt;i;i--)
                putchar(stk[i]|48);
        }
    }
    int read()
    {
        int x=0,f=0;
        char c=getchar();
        while(!isdigit(c))
        {
            f|=c=='-';
            c=getchar();
        }
        while(isdigit(c))
        {
            x=(x<<3)+(x<<1)+(c^48);
            c=getchar();
        }
        return f?-x:x;
    }
    int main()
    {
        return 0;
    }
    程序框架

    结束语

    此篇文章介绍的优化均已实测证明,下面是一些误传:

    1. 迷信inline?实测inline还要稍微慢上一丢丢……
    2. 前置++和后置++实测在单独语句时没有任何差别
    3. 用三目运算符(?:)代替if()else()?然而实测三目运算符比if()else()还要慢一些……
  • 相关阅读:
    D语言中的Range与C#中IEnumreable的区别
    D语言中使用Curl读取网页
    在D语言中如何调用Windows中的COM控件
    D语言反射
    D语言基本类型判断 traits.d
    Struts2 XML验证器
    (十四)Struts2 验证框架
    (十三)Struts2 发送电子邮件
    (十二)Struts2 数据库访问
    (十一)Struts2 文件上传
  • 原文地址:https://www.cnblogs.com/LiHaozhe/p/9520216.html
Copyright © 2011-2022 走看看