在C语言中,整数值用%d输出,实数用 %f 输出。如:
printf("%d\n",1+2);
printf("%.1f\n",8.0/5.0);
一般来说,只要在程序中用到了数学函数,就需要在程序最开始处包含头文件 math.h,并在编译时连接数学库。
scanf 中的占位符和变量的数据类型一一对应,且每个变量前需要加“&”符号。如:scanf("%d%d", &a, &b);
在算法竞赛中,输入前不要打印提示信息。输出完毕后应立即终止程序,不要等待用户按键,因为输入输出过程都是自动的,没有人工干预。
在一般情况下,你的程序不能直接读取键盘和控制屏幕:不要在算法竞赛中使用 getch()、getche()、gotoxy() 和 clrscr() 函数。
在算法竞赛中,每行输出均应以回车符结束,包括最后一行。
尽量用 const 关键字声明常数。如:const double pi = acos(-1.0);
printf 的格式字符串中可以包含其他打印符号,打印时原样输出。如:printf("%d %d\n", a, b);
C99 并没有规定int类型的确切大小,但在当前流行的竞赛平台中,int都是32位整数,范围是-2147483648~2147483647.
在循环体开始处定义的变量,每次执行循环体时会重新声明并初始化。
可以使用 time.h 和 clock() 函数获得程序运行的时间。常数 CLOCKS_PRE_SEC 和操作系统相关,请不要直接使用 clock() 的返回值,而应总是除以 CLOCKS_PRE_SEC。
如果输入的个数不确定,可以这样判断:while(scanf("%d", &x)==1){}
在 Windows 下,输入完毕后先按Enter键,再按 Ctrl+Z 键,最后再按 Enter 键,即可结束输入。在 Linux 下,输入完毕后按 Ctrl+D 键即可结束输入。
变量在未赋值之前的值是不确定的。特别地,它不一定等于0.
使用文件最简单的方法是使用输入输出重定向,只需在 main 函数的入口处加入以下两条语句:
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
比较大的数组应尽量声明在 main 函数外,否则程序可能无法运行。
如果要从数组a赋值k个元素到数组b,可以这样做:memcpy(b, a, sizeof(int)*k)。使用 memcpy 函数要包含头文件 string.h。如果需要把数组a直接复制到数组b中,可以写得简单一些:memcpy(b,a,sizeof(a));
把数组a清零:memset(a,0,sizeof(a)); // 它也在string.h中定义
字符常量可以用单引号法表示。在语法上可以把字符当作int型使用。
在 “scanf(”%s", s)" 中,不要在 s 前面加上 “&” 符号。注意,“scanf(”%s", s)" 遇到空白字符(空格、TAB和回车符)会停下来。
函数 strchr 的作用是在一个字符串中查找单个字符。
printf 输出到屏幕,fprintf 输出到文件,而sprintf 输出到字符串(应当保证字符串足够大,可以容纳输出信息)。
C语言中的字符串是以 “\0” 结尾的字符数组,函数 strlen(s) 的作用是获取字符串 s 的实际长度(结束标记之前的字符个数)。
字符串只能用 strcpy(a, b), strcmp(a, b), strcat(a, b) 来执行”赋值“、”比较“和”连接“操作,而不能用”=“、”==“、”<=“、”+“等运算符。上述函数都在 string.h 中声明。
使用 fgetc(fin) 可以从打开的文件 fin 中读取一个字符。一般情况下应当在检查它不是 EOF (while((c = getchar()) != EOF){...}
)后再将其转换成 char 值。从标准输入读取一个字符可以用 getchar, 它等价于 fgetc(stdin)。
“fgets(buf, maxn, fin)” 将读取完整的一行放在字符数组 buf 中。当一个字符都没有读到时,fgets 返回 NULL。
C语言中的 gets(s) 存在缓冲区漏洞,不推荐使用。在C11标准里,该函数已被正式删除。
善用常量数组往往能简化代码。定义常量数组时无须指明大小,编译器会计算。
头文件 ctype.h 中定义的 isalpha、isdigit、isprint 等工具可以用来判断字符的属性,而 toupper、tolower 等工具可以用来转换大小写。如果ch是大写字母,则 ch-‘A’ 就是它在字母表中的序号(A的序号是0,B的序号是1,以此类推);类似地,如果 ch 是数字,则 ch-‘0’ 就是这个数字的值本身。
在算法竞赛中,请总是让main函数返回0.
为了使用方便,往往用 “typedef struct { 域定义; }类型名;” 的方式定义一个新类型名。这样,就可以像原生数据类型一样使用这个自定义类型。如:typedef struct { double x, y; }Point;
对复杂的表达式进行化简有时不仅能减少计算量,还能减少甚至避免中间结果溢出。
建议把谓词(用来判断某事物是否具有某种特性的函数)命名成 “is_xxx” 的形式,返回 int 值,非0表示真,0表示假。
操作全局变量有风险,应谨慎使用。
变量名前面加"&"得到的是该变量的地址。用 int*a 声明的变量a是指向int型变量的指针。*a 是指”a指向的变量“,而不仅是”a指向的变量所拥有的值“。
以数组为参数调用函数时,实际上只有数组首地址传递给了函数,需要另加一个参数表示元素个数。除了把数组首地址本身作为实参外,还可以利用指针加减法把其他元素的首地址传递给函数。如:
//计算数组的元素和
//法一
int sum(int* a, int n){
int ans = 0;
for(int i = 0; i < n; i++)
ans += a[i];
return ans;
}
//法二 计算左闭右开区间内的元素和
int sum(int* begin, int* end){
int n = end-begin;
int ans = 0;
for(int i = 0; i < n; i++)
ans += begin[i];
return ans;
}
//法三
int sum(int* begin, int* end){
int *p = begin;
int ans = 0;
for(int *p=begin; p!=end; p++)
ans += *p;
return ans;
}
与C程序相比,头文件的变化为:在C头文件之前加一个小写的c字母,然后去掉.h后缀。如:stdio.h 变成了cstdio,string.h变成了 cstring,math.h 变成了cmath,ctype.h 变成了cctype……
algorithm提供了一些常用的算法,如 min。
C++中可以使用流简化输入输出操作。标准输入输出流在头文件iostream中定义,存在于命名空间std中。如果使用了 using namespace std;
语句,则可以直接使用。
声明数组时,数组大小可以使用 const 声明的常数,而不是用 #define 声明常数。
C++中的引用就是变量的”别名“,它可以在一定程度上代替C中的指针。例如,可以用“传引用”的方式(在参数名之前加一个"&"符号)让函数内直接修改实参。
algorithm 头文件中的 sort 可以给任意对象排序,包括内置类型(数组用 sort(a, a+n) 的方式调用,vector 用 sort(v.begin(), v.end() 的方式调用))和自定义类型,前提是类型定义了 “<” 运算符。排序之后可以用 lower_bound 查找大于或等于 x 的第一个位置。unique 函数可以删除有序数组中的重复元素。
vector 头文件里的 vector 是一个不定长数组,可以用 clear() 清空,resize() 改变大小,用 push_back() 和 pop_back() 在尾部添加和删除元素,用 empty() 测试是否为空。vector 之间可以直接赋值或者作为函数的返回值。
set 头文件中的 set(每个元素最多只出现一次)和 map 头文件中的 map(从键(key)到值(value)的映射)分别是集合与映射。二者都支持 insert、find、count 和 remove 操作,并且可以按照从小到大的顺序循环遍历其中的元素。map 还提供了"[]" 运算符,使得 map 可以向数组一样使用。事实上,map 也称为“关联数组”。例如可以用一个 map
STL 的 stack 头文件提供了栈,用 “stack s” 的方式定义,用 push() 和 pop() 实现元素的入栈和出栈操作,top() 取栈顶元素(但不删除)。
STL 的 queue 头文件提供了队列,用 “queue s” 的方式定义,用 push() 和 pop() 实现元素的入队和出队操作,front() 取队首元素(但不删除)。
STL 的 queue 头文件提供了优先队列,用 “priority_queue s” 方式定义,用 push() 和 pop() 实现元素的入队和出队操作,top() 取队首元素(但不删除)。对于一些常见的优先队列,STL 提供了更为简单的定义方法,例如,“越小的整数优先级越大的优先队列”可以写成"priority_queue
cstdlib 中的 rand() 函数可生成闭区间[0, RAND_MAX] 内均匀分布的随机整数,其中 RAND_MAX 至少为32767。如果要生成更大的随机整数,在精度要求不太高的情况下可以用 rand() 的结果“放大”得到。
可以使用 cstdlib 中的 srand 函数初始化随机数种子。如果需要程序每次执行时使用一个不同的种子,可以用 ctime 中的 time(NULL) 为参数调用 srand (即 srang(time(NULL)))。一般来说,只在程序执行的开头调用一次 srand。
随即程序:
void fill_random_int(vector &v, int cnt) {
v.clear();
for(int i = 0; i < cnt; i++)
v.push_back(rand());
}
把 vector 作为参数或者返回值时,应尽量改成引用方式传递参数,以避免不必要的值被复制。
C++ 支持函数重载,但函数的参数类型必须不同(不能只有返回值类型不同)。
**测试时往往使用 assert。**其用法是:“assert(表达式)”,当表达式为假时强行终止程序,并给出错误提示。
可以给结构体重载赋值运算符,使得用起来更方便。
可以给结构体声明一些属于该结构体类型的静态成员变量,方法时加上 static 修饰符。静态成员变量在结构体外部使用时要写成:“结构体名::静态成员变量名”。
如果要在“队列”两端进行插入和删除,可以用 STL 中的双端队列 deque。
简单的表达式解析可以借助栈来完成。
如果要定义一棵二叉树,一般是定义一个“结点”类型的 struct(如叫 Node),然后保存树根的指针(如 Node* root)。
//结点类型
struct Node{
bool have_value; //是否被赋值过
int v; //结点值
Node *left, *right;
Node():have_value(false), left(NULL), right(NULL){} //构造函数
};
Node* root; //二叉树的根结点