当前位置: 首页 > news >正文

信息学竞赛的常数优化、常见问题、代码风格等

如果编译器没有开O2优化
用库函数常数会凭空增加很多。。
似乎NOIP考场不开O2
某些时候,如果你优化到无法再优化的时候
尝试去自己重新实现库函数。

  • isdigit()
  • max()/min()
  • unique()/lower_bound()/upper_bound()
  • scanf()/printf()
  • cin/cout
  • getchar()/putchar()
  • STL::queue/stack/priority_queue/deque

##常数优化:

位运算

没有O2的时候(有O2不用管。编译器会帮你)
x10 <=> (x<<3)+(x<<1)
x!=y <=> x^y
x!=-1 <=> ~x
x
2 <=> x<<1
x*2+1 <=> x<<1|1
x/2 <=> x>>1
(x+1)%2 <=> x^1
x%2 <=> x&1
【upd 2018-11-16】thx to Cwolf9.原文此处有误
x%2==0 <=> !(x&1)

##语法

inline 在非递归函数前加修饰。
循环变量 int i =>register int i

c++没有尾递归优化。所以可以自己手写栈来优化递归。

原则上尽量减少乘/除/取模 指令
取模指令如果是逐渐累加的话,

x+=add;x%=mod;
=> x+=add;x>=mod?x%=mod:1;

A?B:C 好像要比if,else语句快。

memset初始化细节 memset(a,0x3f,sizeof(a));
最后的a[1] = 0x3f3f3f3f
int的极限是 0x7fffffff
还可以~0u
INF有的时候不要刚好赋值到0X7FFFFFFF,如果有2个inf的值相加就会溢出。

乘法溢出。
这个要注意。不要直接全部long long这样慢很多

关于类型转换,一般不用管,编译器会处理。
但是,考试环境有点老?没试过,所以如果有不同类型的话最好在前面显示的强转一下。

赋值>int的话请在数字后加LL

【Update1】2016-11-13

下面的不是绝对,环境不同可能不会出错。
建议平时在编译的时候把编译指令加上 "-ansi"

###信息学竞赛的一些注意事项:[有误请指正]

  • pow()函数请慎用,低版本有的时候会CE。

  • 考场不允许使用**"bits/stdc++.h",并且使用该库变量名可能不能使用next** (C++库里面有个template是next会CE)
    【upt:2018-11-16】听学弟说现在可以用万能头文件了。

  • 请尽力少用黑语法。

  • 二分图匹配避免link做变量名(还有个什么变量名Linux也会CE我突然记不到了…有时其实也可以用**“中国式的变量名命名法”**这样不会CE。 不推荐这种诡异的风格),Linux环境可能会CE。

  • 少用“math.h”|“cmath”库。因为_x,_y,y1,y2,x1,x2,x0,y0,这类命名有时会CE。

  • 考场严禁使用带下划线的库函数。eg. __gcd()

  • 编程时利用宏可以减少代码量,但是请务必在每个变量里加括号。 eg. #define rep(i,s,t) for(int i=(s);i<=(t);i++)

  • 循环变量for(int i;…;…;)请不要放到全局上。这种常数不会卡。相反会带来很多隐式的错误

  • 如果你不精通指针请少用它。指针的代码很难查错。竞赛里面请避免使用函数指针,多级指针,指针数组这样的语法。

  • 如果可以静态实现,请先考虑静态版本的代码。而不是写动态。(malloc() new

  • 引用和指针不是一个东西。这个语法我已经不想解释了。去买本语法书细读。

  • 考试少用C++的OOP特性,可以使用STL template<> class namespace 但不推荐使用。

  • 请熟悉STL里面的 string queue stack vector set map 后面这些用的少,仅供参考并且在pascal选手消失前应该是不会考的前面这些只是方便才用,但请注意常数!推荐自己实现。 deque multiset multimap bitset

  • 宏指令少用,#progma 肯定是禁了的,别想手动扩栈。涉及操作编译器和系统的函数都要挂。

  • 内嵌汇编也是算作弊处理,毕竟这是算法竞赛,不是信息安全竞赛,也不是编程能力竞赛。

下面的话来自一位编程大神有可能我记错了或者大神说错了…
然后有的童鞋认为memset()既然这么容易错,那为什么我们还要用呢,直接for一遍初始化。请注意,memset()底层是用汇编实现的效率要比直接的快4倍,不是所有的库函数都是c\c++实现的。
(我记错了吗…还是strlen() 记不到了QAQ,但是memset实现应该不是直接for,肯定有很多常数优化和位运算)
哦,然后就是strlen() 重点! 像下面这种代码复杂度是o(nL)的,L为str的长度。
for(int i=0;i<strlen(str);i++
已经有很多人还是写的上面的这种代码却一直不知情。等你被卡了就知道了。

最后还有一个问题。由于没有开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); 后。

##代码风格
初学者。
示例:

#include<cstdio>
using namespace std;

int main()
{
    //do sth..
    return 0;
}

上下括号请对齐。请保持缩进。

for(int i=1;i<=n;i++)
{
   for(int j=1;j<=n;j++)
   {
     //do sth..
   }
}

变量名函数名推荐按照 驼峰命名法 eg. checkOfInput()
函数名,变量名最好不要用没有意义的名字。比如,你要检查素数,函数名更好是checkPrime() or isPrime() 这类的,而不是solve()
f() 当然也可以直接check() 但是当你有多个函数的时候为了不让自己混淆请使用最前面的方法。
还有变量名,比如说你要写动态规划,状态数组最好开成dp[][]..
这是大家约定俗成的。这样方便大家互相阅读。也方便别人帮你查错。
还有一些约定俗成的:

dfs()//deep-first-search
bfs()//bread-first-search
maxflow(),dinic()等//最大流
isprime(),getprime()//检查素数,筛素数
getdis()//计算欧几里德距离,曼哈顿距离
query()//查询操作
queryMax()/querySum()
update()//更新操作
tarjan()//有多种tarjan..找强联通分量/双联通分量/LCA的tarjan算法。
LCA()、RMQ()//字面意思..
check()//一般是二分的check()函数
solve()//字面意思..
match()//二分图匹配..
gethash()//字面意思..
getid()//字面意思..
getrank()//字面意思..
sort()//字面意思..
pre()//预处理

dp[][] 一般是dp状态定义 或者f[][]/g[][]
dfn[] dfs序 que[]/q[]/sta[]/s[] 手写栈/队列 head,tail维护首尾。
//边
一般意义下: M->边 N->点 Q->操作数
struct Edge{
    int to,next,w;
}e[M]
struct Edge{
    int u,v,w;
}e[M]
#define maxn ..
#define N ..
#define M ...
#define mod ...
#define max3(a,b,c) max(a,max(b,c))
#define isdigit(x) (x>='0'&&x<='9')
#define lson u<<1
#define rson u<<1|1


  • 代码中插入适当的空格
for(int i = 1; i <= n; i++)
x = (a + b) / 2
ans = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))

当然上面有的地方空格可以略去,看个人习惯。

for(int i=1;i<=n;i++)
for(int i = 1;i <= n; i++)
for(int i = 1; i < = n ; i++)
for(int i = 1; i <= n ; i++)
for(int i=1;i <= n;i++)

#define rep(i,s,t) for(int i=(s);i<=(t);i++)
rep(i,1,n)
  • 请不要在一行做过多的事。
for(int i = 1; i <= n ; i++)scanf("%d",&a[i]),a[i]<0?a[i]=-a[i]:1;

==>
for(int i = 1; i <= n ; i++)
	scanf("%d",&a[i]),a[i]<0?a[i]=-a[i]:1;

for(int i = 1; i <= n ; i++){
	scanf("%d",&a[i]);
	a[i]<0?a[i]=-a[i]:1;
}
for(int i = 1; i <= n ; i++)
{
	scanf("%d",&a[i]);
	if(a[i]<0)a[i] = -a[i];
}
  • 如果同样的计算要出现3次以上请写成函数。
    最简单的例子是getdis(),abs()

这样做的好处是方便调试。
##代码压行
这个是比较有争议的。代码风格好的可以跳过了。刚学的话请别这么干。否则你遇见bug后会放弃治疗。

###为什么要压行?
时间短,浪费在代码风格上无意义。
所以。与上面对立的过程。

for(int i=1;i<=n;i++)
{
	//do sth..
}

========================
for(int i=1;i<=n;i++{
	//do sth..
}
========================
#define rep(i,s,t) for(int i=(s);i<=(t);i++)
rep(i,1,n){
	//do sth..
}
=========================
#define rep(i,t) for(int i=1;i<=(t);i++)
rep(i,t){/*do sth..*/}

===========================
===========================
for(int i=head[u];~i;i=e[i].next){
	//do sth..
}

==============================
#define each(x) for(int i=head[x];~i;i=e[i].next)
each(u){ /*do sth ..*/}

================================
================================
int gcd(int a,int b)
{
	if(!b)return a;
	else return gcd(b,a%b);
}

================================
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
================================
int gcd(int a,int b)
{
	int t;
	while(b!=0)
	{
	    t = a;
	    a = b;
	    b = t%b;
	}
}
===============================
int gcd(int a,int b){for(int t;b!=0;t=a,a=b,b=t%b);}
===============================



相关文章:

  • 【BZOJ 4326】运输计划【树链剖分+差分+二分答案】
  • 【BZOJ 1853】[Scoi2010]幸运数字 【容斥原理】
  • 【BZOJ 1010】【HNOI2008】玩具装箱toy 【斜率优化】
  • 阿狸的英文名
  • 【BZOJ 1857】【SCOI2010】传送带 【三分套三分】
  • 【BZOJ 1012】【JSOI 2008】最大数maxnumber
  • 【BZOJ 1064】【NOI 2008】假面舞会
  • 【BZOJ 1007】【HNOI 2008】水平可见直线 【计算几何】
  • 【BZOJ 1055】【HAOI 2008】玩具取名 【区间DP】
  • 【BZOJ 1068】【SCOI 2007】压缩 【区间DP】
  • 【BZOJ 1090】【SCOI 2003】字符串折叠 【区间DP】
  • 【BZOJ 1196】【HNOI 2006】公路修建问题 【二分+并查集】
  • 【BZOJ 1026】【SCOI 2009】windy数 【数位DP】
  • linux下与windows下的换行符
  • 【BZOJ 1041】【HAOI 2008】圆上的整点 【数学】
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • co.js - 让异步代码同步化
  • Fastjson的基本使用方法大全
  • java 多线程基础, 我觉得还是有必要看看的
  • mac修复ab及siege安装
  • redis学习笔记(三):列表、集合、有序集合
  • SQL 难点解决:记录的引用
  • SQLServer之创建显式事务
  • Vue2.x学习三:事件处理生命周期钩子
  • vue总结
  • 不用申请服务号就可以开发微信支付/支付宝/QQ钱包支付!附:直接可用的代码+demo...
  • 规范化安全开发 KOA 手脚架
  • 扑朔迷离的属性和特性【彻底弄清】
  • 问题之ssh中Host key verification failed的解决
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 最近的计划
  • linux 淘宝开源监控工具tsar
  • 回归生活:清理微信公众号
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • #mysql 8.0 踩坑日记
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • (1) caustics\
  • (ros//EnvironmentVariables)ros环境变量
  • (ZT)北大教授朱青生给学生的一封信:大学,更是一个科学的保证
  • (八)Flask之app.route装饰器函数的参数
  • (二)springcloud实战之config配置中心
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .NET 8 中引入新的 IHostedLifecycleService 接口 实现定时任务
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core WebAPI中封装Swagger配置
  • .net core使用ef 6
  • @Bean注解详解
  • @ModelAttribute使用详解
  • @property python知乎_Python3基础之:property
  • @RequestParam详解