• (持续更新)一些黑科技和技巧


    有疑问或错误尽管评论!!
    下面以C++为准。

    黑科技

    1、读入优化

    在做题时,读入量较大的时候,便可以用来卡常。
    据说GDOI有一题卡读入,题目直接给了学生读入优化。
    Pascal选手应该很无奈……就是过不了。

    void input(int& x)
    {
        char ch=getchar();
        while (ch<'0' || '9'<ch)
            ch=getchar();
        x=0;
        do
        {
            x=x*10+ch-48;
            ch=getchar();
        }
        while ('0'<=ch && ch<='9');
    }
    

    还有个东西叫输出优化,可以自己下去打,但建议不用——因为只输出一个数的题太多了。

    2、max、min的优化

    我们知道,打max、min时,要用分支(if语句)。这样会使程序超慢。
    有没有其他方法?有的。
    当x<0时 x>>31=-1 (11111111111111111111111111111111)
    当x>=0时 x>>31=0(00000000000000000000000000000000)
    所以我们可以通过作差,求出谁大谁小。

    int max(int a,int b)
    {
    	int m=(a-b)>>31;
    	return a&~m|b&m;
    }
    int min(int a,int b)
    {
    	int m=(a-b)>>31;
    	return a&m|b&~m;
    }
    

    以max举例,
    当a>=b时,m=0,所以max(a,b)=a&~0|b&0=a&-1|b&0=a
    当a< b时,m=-1,所以max(a,b)=a&~-1|b&-1=a&0|b&-1=b

    补上一个abs的优化:

    int abs(int a)
    {
    	int b=a>>31;
    	return (a+b)^b;
    }
    

    当a>=0时 b=0 abs(a)=a^0=a
    当a<0时 b=-1 abs(a)=(a-1)^-1=-a(相信大家都懂补码的转换方式)

    有一点很重要的是,不要乱用!比如不能硬是将int改为long long,注意右移的位数要变!

    3、手动编译优化

    比赛时不要用!!!

    #pragma GCC optimize("O3")
    #pragma G++ optimize("O3")
    

    你可以将O3改为O2,O1,O。

    4、#define

    格式:#define x y
    在程序中,一切出现x的地方都会变成y。
    可以省码量,增强可读性。
    有种带参数的,在名字(x)后打空格,里面写参数(用逗号隔开,不用标类型)
    例如#define max(a,b) ((a)>(b)?(a):(b))
    但是要记住它的本质,它只是单纯的替换。若不加括号,也许会出现各种运算顺序的错误。还有,不要将长的式子、函数、++或–放进去。不然会计算多遍,时间也许会炸。
    取消宏定义:#undef x
    不解释
    还有其它的不怎么会用到,有兴趣的同学可以上网搜。
    补上懒人的文件输入输出:

    #define I_O(x) freopen(""#x".in","r",stdin);freopen(""#x".out","w",stdout);
    

    用的时候码上I_O(文件名),不加双引号。

    5、cstring中常用的函数

    这些函数应该人人都会,但还是有好多人不会。

    先说一下指针与数组的关系。
    若有数组int a[N];
    则a表示a[0]的地址(&a[0])
    *a即是a[0]
    a+i=&a[i]
    *(a+i)=a[i]

    memset(指针(数组名),数值(最大127,最小128,清零0),大小(sizeof ……))
    用法就是将一数组初始化。
    memcpy(指针A,指针B,大小SIZE)
    将B出复制SIZE这么多的内存到A处。

    6、排序

    包含algorithm

    sort(指针begin,指针end)
    将begin到end-1的元素以operator<进行快速排序。
    sort(begin,end,cmp)
    将begin到end-1的元素以cmp进行快速排序。
    有的孩子不知道cmp咋搞。
    比如从大到小排序

    bool cmp(int a,int b)
    {
    	return a>b;
    }
    

    a代表前面的元素,b代表后面的元素。表示排序后的序列满足a>b!

    稳定性排序:
    stable_sort(begin,end);
    steble_sort(begin,end,cmp);

    7、堆

    queue里有一个,但我不爱用,因为内部一定有许多繁杂的操作,比如指针开辟一个存储空间,会使程序变慢。
    我用algorithm里的堆。
    先注意一下比较函数int cmp(int a,int b)
    a表示后代,b表示祖先,满足一个这样的顺序。(可以理解为大根堆)
    make_heap(begin,end)
    将begin到end-1的元素变成大根堆
    make_heap(begin,end,cmp)
    将begin到end-1的元素以cmp的顺序变成大根堆
    push_heap(begin,end)
    push_heap(begin,end,cmp)
    前面begin到end-2已满足堆的性质,将end-1的元素放进堆
    pop_heap(begin,end)
    pop_heap(begin,end,cmp)
    将begin的元素弹出,移至end-1处。

    8、bitset

    包含bitset
    这是一个C++的压位神器,可以方便的做一些与压位有关的事情。比如状压DP。
    定义:bitset<n> b
    你可以将它当做一个bool数组,但它可以进行位运算。
    一些有用的函数
    b.count()返回b中1的个数
    b.to_ulong()将其变成unsigned long类型

    9、调试时检查范围溢出

    在编译选项中加上-ftrapv

    技巧

    1、减少/或%运算

    除法运算的时间是+、-的20倍左右,所以尽量不用。
    举个例子:

    a=n/m;
    b=n%m;
    

    我们可以将其改为

    a=n/m;
    b=n-a*m;
    

    这样将会快很多。
    我们可以用位运算来代替对2次幂的除法运算。
    x/2=x>>1
    x%2=x&1
    还有些要取模的程序,可以隔几个模一次,这样能取得良好的效果(有很多同学因此被卡)。

    2、关于高维数组

    首先要知道,有个叫缓存的东西。例如你在访问a[i][j]时,a[i][j+1]就已经在缓存里了。但a[i+1][j]并不在缓存里。所以,最好按顺序访问数组内的元素。

    还有高维数组的的寻址也极为耗时,所以最好用指针或引用变量。

    高维数组的下标最好不要设成2次幂,因为计算机用2进制,使用这些会频繁地发生冲突,降低时间效率。

    3、全局变量和局部变量

    全局变量和局部变量,哪个更好?
    全局变量的好处是方便各个函数使用,而且可以放一些比较大的数据,还有方便初始化(开始全为0)。
    而局部变量的好处是快,因为它们可以享受到快速的栈空间,但不能存储较大的数据,而且必须初始化!

    4、for循环

    常规格式:
    for (i=begin;i<=end;i++)
    有一点要注意,end这个区域尽量不要用算式(尤其是结果在循环中不会变的那种),最好先算出来,再进入循环。不然,你循环多少次,它就算多少次。
    举个例子,小时候的判断素数的程序:

    bool ss(int n)
    {
    	int i;
    	for (i=2;i<=int(sqrt(n));i++)
    		if (n%i==0)
    			return 0;
    	return 1;
    }
    

    但是这个程序是低效的。因为i循环多少次,就进行多少次开方计算。开方计算大概是加法的60倍,乘法的30倍。
    改进1:

    bool ss(int n)
    {
    	int i;
    	for (i=2;i*i<=n;i++)
    		if (n%i==0)
    			return 0;
    	return 1;
    }
    

    这将快很多。因为它用了乘法替换了开方。

    bool ss(int n)
    {
    	int m=int(sqrt(n)),i;
    	for (i=2;i<=m;i++)
    		if (n%i==0)
    			return 0;
    	return 1;
    }
    

    若是小数据,那么也许会比上一个慢。但如果是大数据,则会快一些。

    5、减少分支

    if是很慢的。在程序中,能省就省。
    举个例子:

    for (i=1;i<=n;i++)
    	if (i!=m)
    	{
    		/*code*/
    	}
    

    有些人打程序常常会这样,这就进行了n次if语句。
    不妨这样打:

    for (i=1;i<m;i++)
    {
    	/*code*/
    }
    for (i=m+1;i<=n;i++)
    {
    	/*code*/
    }
    

    这就省了n个if。

  • 相关阅读:
    c# yield关键字原理详解
    Linux环境基于CentOS7 搭建部署Docker容器
    关于c#中委托使用小结
    推荐一本好的c#高级程序设计教程
    WEB网站常见受攻击方式及解决办法
    判断URL是否存在
    提升高并发量服务器性能解决思路
    分享asp.net学习交流社区
    js中对arry数组的各种操作小结
    jQuery动态实现title的修改 失效问题
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145208.html
Copyright © 2020-2023  润新知