ACM卡常粗谈

21 篇文章 1 订阅

一、为什么要卡常数?

    OI中数据结构与常数优化关系很大的

    如果你常数好可以暴力过数据结构题啦~

    如果你常数不好即使复杂度一样也会被出题人卡~

二、常用的卡常数方法

    1、卡IO(输入输出)

       至于输入输出挂,我就不贴了,网上都有很多很多

    2、卡编译

      2.1 C++内联函数inline

      讲真,inline挺神奇的,加上就可以优化函数/过程的常数
      不过,是针对非递归形式的

      编译器在编译时会在主程序中把函数的内容直接展开替换,减少了内存访问

int max(int a, int b){return a>b?a:b;}//原函数
inline int max(int a, int b){return a>b?a:b;}//前面直接加inline就好了。

     2.2 CPU寄存器变量register

     对于一些频繁使用的变量,可以声明时加上该关键字,运行时可能会把该变量放到CPU寄存器中,只是可能,不保证有效。

     特别是你变量多的时候,一般还是丢到内存里面的。 
     比较下面两段程序:

register int a=0;
for(register int i=1;i<=999999999;i++)a++;
int a=0;
for(int i=1;i<=999999999;i++)a++;

    优化:0.2826 second 
    不优化:1.944 second

3、卡计算

3.1 取模优化

    为了进一步优化程序代码,经常要对除法,浮点等运算进行优化,这里给出整形变量之间%计算的优化:

    f(x,y) = x%y;

    优化如下:

    f(x,y) = x - y * (x/y);

    写成函数如下:

int MOD(int x, int y){
	return x - y * (x / y);
}

   对2的幂取模(2^k),实际等同于和(2^k - 1)进行“位与”操作

   例如对8取模,因为计算机内部是二进制表示,所以可以对二进制位进行位与操作

int mod(int x,int y){
   x=x&(pow(2,y)-1);
}

3.2 加法优化

   用++i代替i++。 
   1. 后置++需要保存临时变量以返回之前的值,在 STL 中非常慢。 
   2. 事实上,int的后置++ 在实测中也比前置++ 慢0.5倍左右(UOJ 上自定义测试)

3.3 数组优化

   1、不要开bool,所有bool改成char,int是最快的(原因不明)。 
   2、在最大值50000*50000的时候用unsigned代替long long 
   3、对于多维数组,用到乘法优化寻址

3.4 判断优化
   if()else语句比()?():()语句要慢,逗号运算符比分号运算符要快。

3.5 结构优化
   1.如果你要经常调用a[x],b[x],c[x]这样的数组,把她们写在同一个结构体里面会变快一些,比如f[x].a, f[x].b, f[x].c 
   2.指针比下标快。
 

4、卡算法/预处理

4.1 倍增优化

倍增表小的开前面——寻址会变快很多

算了,反正我不是打dp位的

4.2 Floyd初始化

   

for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
        gra[i][j]=inf
for(int i=1;i<=b;i++)
    gra[i][i]=0

是比

for(int i=1;i<=n;i++)
   for(int j=1;j<=n;j++){
      if(i==j) gra[i][j]=0;
      else gra[i][j]=inf
   } 

快80%以上的,,因为要每次去比较。   

4.4 循环展开

void Init_Array(int *dest, int n)
{
    int i;
    for(i = 0; i < n; i++)
        dest[i] = 0;
}



void Init_Array(int *dest, int n)
{
    int i;
    int limit = n - 3;
    for(i = 0; i < limit; i+= 4)//每次迭代处理4个元素
    {
        dest[i] = 0;
        dest[i + 1] = 0;
        dest[i + 2] = 0;
        dest[i + 3] = 0;
    }
    for(; i < n; i++)//将剩余未处理的元素再依次初始化
        dest[i] = 0;
}

 

常数优化:位运算

  没有O2的时候(有O2不用管。编译器会帮你)
  x10 <=> (x<<3)+(x<<1)
  x!=y <=> x^y
  x!=-1 <=> ~x
  x2 <=> x<<1
  x*2+1 <=> x<<1|1
  x/2 <=> x>>1
  (x+1)%2 <=> x^1
  x%2 <=> x&1
  x%2==0 <=> !(x&1)

还有一个问题。由于没有开O2优化,会导致一些本来没有区别的变得比较明显。多维数组请把大的放前面。eg. int dp[10000][10][2] 而不是 dp[2][10][10000],常数差距0.5s。比算法的差距还大。开了o2后差别不明显。

还有一个坑。那些将cin/cout和scanf()/printf()一起用的朋友们,如果你们的代码再怎么查都查不出错了,这个时候要考虑是不是把c++和c的IO函数混用了。这个会导致一些潜在性的问题。尤其是当你用了ios::sync_with_stdio(false); 后。
 

 

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值