参考:《算法竞赛入门经典》
一、浮点数的问题(尽量避免浮点运算)
浮点数的运算(或函数)有可能存在误差——不是一定存在,但经常都会
比如在经过大量计算后,由于误差的影响,整数 1 变成了 0.9999999999
1、floor 向下取整
floor(3.14) = 3
floor(9.999999) = 9
floor(-3.14) = -4
floor(-9.999999) = -10
floor(1.0) = 1
floor(x + 0.5) 即将下取整改为四舍五入形式
2、ceil 向上取整
floor(3.14) = 4
floor(9.999999) = 10
floor(-3.14) = -3
floor(-9.999999) = -9
floor(1.0) = 1
二、一些数学公式
1、三角形面积公式
海伦公式
p = (a + b + c) / 2.0
S = sqrt(p * (p - a) * (p - b) * (p - c))
2、圆台体积公式:(1.0/3.0)* PI * h * (R * R+r * r+R * r)
π的宏定义
#define PI acos(-1.0)
#define PI (atan(1.0) * 4.0)
三、
如果要从数组 a 复制 k 个元素到数组 b,可以这样做:memcpy(b, a, sizeof(int) * k)
当然了,如果数组 a 和 b 都是浮点型的,复制时要写成 memcpy(b, a, sizeof(double) * k)
如果需要把数组 a 全部复制到数组 b 中,可以写得简单一些:memcpy(b, a, sizeof(a))
四、
C 语言中,有一些字符直接表示出来不方便,例如回车符——它是 '\n',而空字符是 '\0',它也是 C 语言中字符串的结束标志
其他例子包括 '\\'(注意必须有两个反斜线)、'\''(这个是单引号),甚至还有的字符有两种写法:'\"' 和 '"' 都表示双引号
像这种以反斜线开头的字符称为转义序列
五、
strchr 函数:char *strchr(const char* _Str,char _Val)
功能:查找字符串_Str中首次出现字符_Val的位置
说明:返回首次出现_Val的位置的指针,返回的地址是被查找字符串指针开始的第一个与Val相同字符的指针,如果Str中不存在Val则返回NULL。
strcat 函数原型:extern char *strcat(char *dest, const char *src)
功能:把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串
返回指向dest的指针
六、
用编译选项 -Wall 编译程序时,会给出很多(但不是所有)警告信息,以帮助程序员查错
七、用 fgets 而不是 gets
gets 和它的兄弟 fgets 差别比较大:它的用法是 gets(s),没有指明读取的最大字符数
这里就出现了一个潜在的问题:gets 将不停地往 s 里塞东西,而不管塞不塞得下!难道 gets 函数不去管 s 的可用空间有多少吗?你还真说对了
提示1:
C 语言并不禁止程序读写“非法内存”
例如你声明的是 char s[100],你完全可以赋值 s[10000] = 'a'(甚至 -Wall 也不会警告),但后果自负
提示2:
C 语言中的 gets(s) 存在缓冲区溢出漏洞,不推荐使用
解决“输入中有空格”的问题,我们选择的是 fgets 函数,它可以一次性读取一整行,最为方便
例:求最长回文子串
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>
using namespace std;
#define REP(i, n) for (int i = 0; i < (n); ++i)
typedef long long ll;
typedef pair<int, int> Pair;
const int INF = 0x7fffffff;
const int maxn = 5000 + 10;
char buf[maxn], str[maxn];
int pos[maxn];
int main() {
#ifdef __AiR_H
freopen("in.txt", "r", stdin);
#endif // __AiR_H
while (fgets(buf, sizeof(str), stdin)) {
int cnt = 0, Max = 0, Left = 0, Right = 0;
int len = strlen(buf);
REP(i, len) {
pos[cnt] = i;
if (isalpha(buf[i])) { str[cnt++] = toupper(buf[i]); }
}
REP(i, cnt) {
for (int j = 0; i - j >= 0 && i + j < cnt; ++j) {
if (str[i - j] != str[i + j]) { break; }
if (j * 2 + 1 > Max) {
Max = j * 2 + 1; Left = pos[i - j]; Right = pos[i + j];
}
}
for (int j = 0; i - j >= 0 && i + j + 1 < cnt; ++j) {
if (str[i - j] != str[i + j + 1]) { break; }
if (j * 2 + 2 > Max) {
Max = j * 2 + 2; Left = pos[i - j]; Right = pos[i + j + 1];
}
}
}
for (int i = Left; i <= Right; ++i) {
printf("%c", buf[i]);
}
printf("\n");
}
#ifdef __AiR_H
printf("Time used = %.2fs\n", (double)clock() / CLOCKS_PER_SEC);
#endif // __AiR_H
return 0;
}
七、
printf("%d %o %x\n", a, a, a);
将把整数 a 分别按照十进制、八进制和十六进制输出
八、编写函数时,应尽量保证它能对任何合法参数都能得到正确的结果。如若不然,应在显著位置标明函数的缺陷,以避免误用
例、孪生素数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>
#include <cassert>
using namespace std;
#define REP(i, n) for (int i = 0; i < (n); ++i)
typedef long long ll;
typedef pair<int, int> Pair;
const int INF = 0x7fffffff;
bool is_prime(int x);
int main() {
#ifdef __AiR_H
freopen("in.txt", "r", stdin);
#endif // __AiR_H
int m;
scanf("%d", &m);
for (int i = m - 2; i >= 3; --i) {
if (is_prime(i) && is_prime(i + 2)) {
printf("%d %d\n", i, i + 2); break;
}
}
#ifdef __AiR_H
printf("Time used = %.2fs\n", (double)clock() / CLOCKS_PER_SEC);
#endif // __AiR_H
return 0;
}
bool is_prime(int x) {
assert(x >= 0);
if (x == 1) { return false; }
int m = floor(sqrt(x) + 0.5);
for (int i = 2; i <= m; ++i) {
if (x % i == 0) { return false; }
}
return true;
}
除了特判 n == 1 的情况,程序中还使用了变量 m,一方面避免了每次重复计算 sqrt(x)
另一方面也通过四舍五入避免了浮点误差——如果 sqrt “不小心”把某个本应是整数的值弄成了 xxx.99999,也将被修正
但直接写 m = sqrt(x) 的话那个 “.99999” 会被无情地截掉
最后,程序使用了 cassert 中的 assert 宏来限制非法的函数调用:当 x >= 0不成立时,程序将异常终止,并给出提示信息
检查非法参数是很有用的的:当算法很复杂时,一不小心就会用非法参数调用某些自定义函数,如果函数不对参数进行检查,则很可能让程序得到一个荒唐的结果
如果函数调用关系非常复杂,是难以查出错误的根源的
如果每个函数都检查参数,就能较快地找出“罪魁祸首”
提示:编程时合理地利用 assert 宏,将给调试带来很大的方便
总而言之,在实际的系统中,“一个地方的参数错误就引起整个程序异常退出”是不可取的,在编写和调试算法程序中,assert 会“迫使”我们编写出更高质量的程序
九、Vijos P1333 Cantor表
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>
#include <cassert>
using namespace std;
#define REP(i, n) for (int i = 0; i < (n); ++i)
typedef long long ll;
typedef pair<int, int> Pair;
const int INF = 0x7fffffff;
int main() {
#ifdef __AiR_H
freopen("in.txt", "r", stdin);
#endif // __AiR_H
int N;
while (scanf("%d", &N) != EOF) {
int s = 0, k = 1;
while (1) {
s += k;
if (s >= N) {
if (k & 1) {
printf("%d/%d\n", s - N + 1, k - s + N); break;
}
printf("%d/%d\n", k - s + N, s - N + 1); break;
}
++k;
}
}
#ifdef __AiR_H
printf("Time used = %.2fs\n", (double)clock() / CLOCKS_PER_SEC);
#endif // __AiR_H
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>
#include <cassert>
using namespace std;
#define REP(i, n) for (int i = 0; i < (n); ++i)
typedef long long ll;
typedef pair<int, int> Pair;
const int INF = 0x7fffffff;
int main() {
#ifdef __AiR_H
freopen("in.txt", "r", stdin);
#endif // __AiR_H
int N;
while (scanf("%d", &N) != EOF) {
int k = (int)floor((sqrt(8.0 * N + 1) - 1) / 2 - 1e-9) + 1;
int s = k * (k + 1) / 2;
if (k & 1) {
printf("%d/%d\n", s - N + 1, k - s + N); continue;
}
printf("%d/%d\n", k - s + N, s - N + 1);
}
#ifdef __AiR_H
printf("Time used = %.2fs\n", (double)clock() / CLOCKS_PER_SEC);
#endif // __AiR_H
return 0;
}
十、三角形有向面积
注意:树的坐标 (x,y) 的值为区间 [1, 99] 内的整数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <set>
#include <cmath>
#include <cctype>
#include <ctime>
#include <cassert>
using namespace std;
#define REP(i, n) for (int i = 0; i < (n); ++i)
#define eps 1e-9
typedef long long ll;
typedef pair<int, int> Pair;
const int INF = 0x7fffffff;
double x_0, y_0, x_1, y_1, x_2, y_2;
int x_left, y_left, x_right, y_right;
double area2(double _x_0, double _y_0, double _x_1, double _y_1, double _x_2, double _y_2) {
return (_x_0 * _y_1 - _x_1 * _y_0) + (_x_1 * _y_2 - _x_2 * _y_1) + (_x_2 * _y_0 - _x_0 * _y_2);
}
bool judge(double x, double y);
int main() {
#ifdef __AiR_H
freopen("in.txt", "r", stdin);
#endif // __AiR_H
while (scanf("%lf %lf %lf %lf %lf %lf", &x_0, &y_0, &x_1, &y_1, &x_2, &y_2) != EOF) {
if (x_0 + y_0 + x_1 + y_1 + x_2 + y_2 < eps) { break; }
double t = min(x_0, min(x_1, x_2)); x_left = max(1, (int)ceil(t));
t = max(x_0, max(x_1, x_2)); x_right = min(99, (int)floor(t));
t = min(y_0, min(y_1, y_2)); y_left = max(1, (int)ceil(t));
t = max(y_0, max(y_1, y_2)); y_right = min(99, (int)floor(t));
int ans = 0;
for (int i = x_left; i<= x_right; ++i) {
for (int j = y_left; j <= y_right; ++j) {
if (judge((double)i, (double)j)) { ++ans; }
}
}
printf("%4d\n", ans);
}
#ifdef __AiR_H
printf("Time used = %.2fs\n", (double)clock() / CLOCKS_PER_SEC);
#endif // __AiR_H
return 0;
}
bool judge(double x, double y) {
double t = fabs(area2(x_0, y_0, x_1, y_1, x_2, y_2));
double a = fabs(area2(x, y, x_1, y_1, x_2, y_2));
double b = fabs(area2(x_0, y_0, x, y, x_2, y_2));
double c = fabs(area2(x_0, y_0, x_1, y_1, x, y));
if (fabs(t - a - b - c) < eps) { return true; }
return false;
}