算法竞赛入门经典(第2版)

第1部分 语言篇

第1章 程序设计入门

1.1 算术表达式

程序1-1 计算并输出1+2的值
#include<stdio.h>
int main()
{
    printf("%d\n", 1 + 2);
    return 0;
}
程序1-2 计算并输出8/5的值,保留小数点后1位
#include <stdio.h>
int main()
{
	printf("%.1f\n", 8.0 / 5.0);
	return 0;
}
程序1-3 复杂表达式的计算

计算 1 + 2 3 5 − 0.1 1+\frac{2\sqrt{3}}{5-0.1} 1+50.123 的值:

#include<stdio.h>
#include<math.h>
int main()
{
	printf("%.8f\n", 1 + 2 * sqrt(3) / (5 - 0.1));
	return 0;
}

1.2 变量及及其输入

程序1-4 a+b问题
#include<stdio.h>
int main()
{
	int a, b;
	scanf("%d%d", &a, &b);
	printf("%d\n", a + b);
	return 0;
}
例题1-1 圆柱体的表面积

输入底面半径r和高h,输入圆柱体的表面积,保留3位小数。
样例输入:
3.5 9
样例输出:
Area = 274.889

#include<stdio.h>
#include<math.h>
int main()
{
	const double pi = acos(-1.0);
	double r, h, s1, s2, s;
	scanf("%lf%lf", &r, &h);
	s1 = pi * r * r;
	s2 = 2 * pi * r * h;
	s = s1 * 2 + s2;
	printf("Area = %.3lf\n", s);
	return 0;
}

math.h中定义的常量M_PI不是ANSI C标准,使用gcc-ansi编译会出错,所以使用acos(-1.0) 求取 π \pi π的值。double acos(double x)返回以弧度表示x的余弦值(弧度区间为[0, π \pi π]),x为介于[-1,1]的浮点值。

1.3 顺序结构程序设计

例题1-2 三位数反转

输入一个三位数,分离出它的百位、十位和个位,反转后输出。
样例输入:
127
样例输出:
721

#include<stdio.h>
int main()
{
	int n;
	scanf("%d", &n);  // 若输入520
	printf("%d%d%d\n", n % 10, n / 10 % 10, n / 100);                 // 输出025
	printf("%d\n", (n % 10) * 100 + (n / 10 % 10) * 10 + n / 100);    // 输出25
	printf("%03d\n", (n % 10) * 100 + (n / 10 % 10) * 10 + n / 100);  // 输出025
}
例题1-3 交换变量

输入两个整数a和b,交换二者的值,然后输出:
样例输入:
824 16
样例输出:
16 824

#include<stdio.h>

// 借助临时变量
void swap1(int& a, int& b)
{
	int t = a;
	a = b;
	b = t;
}

// 不借助临时变量
void swap2(int& a, int& b)
{
	a = a + b;
	b = a - b;
	a = a - b;
}

int main()
{
	int a, b, t;
	scanf("%d%d", &a, &b);
	
	swap1(a, b);
	printf("%d %d\n", a, b);  // 1
	swap1(a, b);
	
	swap2(a, b);
	printf("%d %d\n", a, b);  // 2
	swap2(a, b);
	
	printf("%d %d\n", b, a);  // 3
}

算法竞赛是比谁能更好地解决问题,而不是比谁写的程序看上去更高级,应该保持简单(Keep It Simple and Stupid, KISS),所以本题最适合的程序是法3。

1.4 分支结构程序设计

例题1-4 鸡兔同笼

已知鸡和兔的总数量为n,总腿数为m。输入n和m,依次输出鸡和兔的数目。如果无解,则输出No answer。
样例输入:
14 32
样例输出:
12 2
样例输入:
10 16
样例输出:
No answer

#include<stdio.h>
int main()
{
	int a, b, n, m;
	scanf("%d%d", &n, &m);
	a = (n * 4 - m) / 2;
	b = n - a;
	if(m % 2 == 1 || a < 0 || b < 0)
		printf("No answer\n");
	else
		printf("%d %d\n", a , b);
	return 0;
}

C语言逻辑表达式的短路(short-circuit)策略,当a为真时表达式a||b不再计算b的值直接返回真,当a为假时表达式a&&b不再计算b的值直接返回假。

例题1-5 三整数排序

输入3个整数,从下到大排序后输出。
样例输入:
20 7 33
样例输出:
7 20 33

#include<stdio.h>
int main()
{
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	
	if(a <= b && b <= c) printf("%d %d %d\n", a, b, c);
	else if(a <= c && c <= b) printf("%d %d %d\n", a, c, b);
	else if(b <= a && a <= c) printf("%d %d %d\n", b, a, c);
	else if(b <= c && c <= a) printf("%d %d %d\n", b, c, a);
	else if(c <= a && a <= b) printf("%d %d %d\n", c, a, b);
	else if(c <= b && b <= a) printf("%d %d %d\n", c, b, a);
   
   	int t;
   	if(a > b) { t = a; a = b; b = t; } // 执行完毕后a<=b
   	if(a > c) { t = a; a = c; c = t; } // 执行完毕后a<=c,且a<=仍然成立
   	if(b > c) { t = b; b = c; c = t; } // 执行完毕后a<=b<=c
   	printf("%d %d %d\n", a, b, c);
 
 	return 0;
}

1.5 注解与习题

习题1-1 平均数(average)

输入3个整数,输出他们的平均值,保留3位小数。

#include<stdio.h>
int main()
{
	int a, b, c;
	double d;
	scanf("%d%d%d", &a, &b, &c);
	d = (double)(a + b + c);
	printf("%.3lf\n", d / 3.0);
	return 0;
}
习题1-2 温度(temperature)

输入华氏温度f, 输出对应的摄氏温度c,保留3位小数。提示: c = 5 ( f − 32 ) / 9 c = 5(f - 32) / 9 c=5(f32)/9

#include<stdio.h>
int main()
{
	int f;
	double c;
	scanf("%d", &f);
	c = 5 * (f - 32) / 9.0;
	printf("%.3lf\n", c);
	return 0;
}
习题1-3 连续和(sum)

输入正整数n, 输出1+2+···+n的值。提示:目标是解决问题,而不是练习编程。

#include<stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if(n % 2 == 0)
		printf("%d\n", n / 2 * (n + 1));
	else
		printf("%d\n", (n + 1) / 2 * n);
	return 0;
}
习题1-4 正弦和余弦(sin和cos)

输入正整数n(n<360),输出n的正弦、余弦函数值。提示:使用科学函数。

#include<stdio.h>
#include<math.h>
int main()
{
	int n;
	double angle;
	const double pi = acos(-1.0); 
	scanf("%d", &n);
	angle = n / 180.0 * pi;
	printf("%lf %lf\n", sin(angle), cos(angle));
	return 0;
}
习题1-5 打折(discount)

一件衣服95元,若消费满300元,可打八五折。输入购买衣服件数,输出需要支付的金额(单位:元),保留两位小数。

#include<stdio.h>
int main()
{
	int n;
	double prize;
	scanf("%d", &n);
	prize = 95.0 * n;
	if(prize >= 300)
		printf("%.2lf\n", prize * 0.85);
	else
		printf("%.2lf\n", prize);
	return 0;
}
习题1-6 三角形(triangle)

输入三角形3条边的长度值(均为正整数),判断是否能为直角三角形的3个变长。如果可以,则输出yes,如果不能则输出no。如果根本无法构成三角形,则输出not a triangle。

#include<stdio.h>
int main()
{
	int a, b, c, t;
	scanf("%d%d%d", &a, &b, &c);
	if(a > b) { t = a; a = b; b = t; }
	if(a > c) { t = a; a = c; c = t; }
	if(b > c) { t = b; b = c; c = b; }
	if(a + b <= c)
		printf("not a triangle\n");
	else if(a * a + b * b == c * c) 
		printf("yes\n");
	else
		printf("no\n");
	return 0;
}
习题1-7 年份(year)

输入年份,判断是否为闰年。如果是,则输出yes,否则输出no。
提示:简单地判断除以4的余数是不够的。

#include<stdio.h>
int main()
{
	int n;
	scanf("%d", &n);
	if(n % 400 == 0 || (n % 100 != 0 && n % 4 == 0))
		printf("yes\n");
	else
		printf("no\n");
}

第2章 循环结构程序设计

2.1 for循环

程序2-1 输出1,2,3…,n的值
#include<stdio.h>                 // 1
int main()                        // 2
{                                 // 3
	int n;                        // 4
	scanf("%d", &n);              // 5
	for(int i = 1; i <= n; i++)   // 6
		printf("%d\n", i);        // 7
	return 0;                     // 8
}                                 // 9

for循环的格式为:for(初始化; 条件; 调整) 循环体;
初始化–>条件–>循环体–>调整–>条件–>循环体–>…->调整–>条件不满足 -->退出循环

输入3的执行过程:
⓵ 当前行5:scanf请求键盘输入,输入2,此时变量n=2;
⓶ 当前行6:第一次执行循环语句,执行初始化语句int i = 1,条件i <= 1满足,继续执行
⓷ 当前行7:执行printf,由于i=1,屏幕输出1并换行。循环体结束跳回第6行
⓸ 当前行6:先执行调整语句i++,此时i=2,n=2,条件i<=n满足,继续执行.
⓹ 当前行7:执行printf,由于i=2,屏幕输出2并换行。循环体结束跳回第6行
⓺ 当前行6:先执行调整语句i++,此时i=3,n=2,条件i<=n不满足,跳出循环体。
⓻ 当前行8:程序结束。

例题2-1 aabb

输出所有形如aabb的4位完全平方数(即前两位数字相等,后两位数字也相等)。

#include <stdio.h>
#include <math.h>
int main()
{
    for (int a = 1; a < 10; a++)
        for (int b = 0; b < 10; b++)
        {
            int n = a * 1100 + b * 11;
            int m = floor(sqrt(n) + 0.5);
            if (m * m == n)
                printf("%d\n", n);
        }
    return 0;
}

if(sqrt(n)==floor(sqrt(n)))判断sqrt(n)是否为整数不保险,因为浮点数的运算(和函数)可能存在误差假设经过大量计算后由于误差影响整数1变成了0.9999999999,floor的结果会是0而不是1为了减少误差影响一般改为四舍五入,即floor(x+0.5),但是小数部分为0.5的数也会受到浮点数误差的影响
例如printf("%lf\n", floor(1.9999999999999999));的结果为2.000000。

另一个思路是枚举平方根,避免开平方操作:

#include <stdio.h>
#include <math.h>
int main()
{
    for (int x = 0;; x++)
    {
        int n = x * x;
        if (n < 1000)
            continue;
        if (n > 9999)
            break;
        int hi = n / 100;
        int lo = n % 100;
        if (hi / 10 == hi % 10 && lo / 10 == lo % 10)
            printf("%d\n", n);
    }
}

死循环for(;;),循环体不加break就不会结束。

2.2 while循环和do-while循环

例题3n+1问题

猜想:对于任意大于1的自然数n,若n为奇数,则将n变为3n+1,否则变为n的一半。经过若干次这样的变换,一定会使n变为1。例如,3–>10->5–>16–>8–>4–>2–>1。
输入n,输出变换的次数, n ≤ 1 0 9 n\leq10^9 n109
样例输入:
3
样例输出:
7

// 3n+1问题(有bug)
#include <stdio.h>
int main()
{
    int n, count = 0;
    scanf("%d", &n);
    while (n != 1)
    {
        if (n % 2 == 0)
            n = n / 2;
        else
            n = 3 * n + 1;
        count++;
    }
    printf("%d\n", count);
}

输入测试示例987654321,当判断条件为n!=1时进入死循环,当判断条件为n>1时输出1,原因是int值是-2147483648~2147483647,而3*n+1超出了int值范围

#include <stdio.h>
#include <math.h>
int main()
{
    // [-2147483648~2147483647]
    printf("[%d~%d]\n", 0x80000000, 0x7fffffff);
    printf("[%d~%d]\n", 0b10000000000000000000000000000000, 0b01111111111111111111111111111111);
    printf("[%d~%d]\n", (int)(-(long)pow(2, 31)), (int)((long)pow(2, 31) - 1));
}

2.3 循环的代价

2.4 算法竞赛中的输入输出框架

2.5 注解与习题

第3章 数组和字符串

3.1 数组

3.2 字符数组

3.3 竞赛题目选讲

3.4 注解与习题

第4章 函数和递归

4.1 自定义函数和结构体

4.2 函数调用与参数传递

4.2.1 形参与实参
4.2.2 调用栈
4.2.3 用指针作参数
4.2.4 初学者易犯的错误
4.2.5 数组作为参数和返回值
4.2.6 把函数作为函数的参数

4.3 递归

4.3.1 递归定义
4.3.2 递归函数
4.3.3 C语言对递归的支持
4.3.4 段错误与栈溢出

4.4 竞赛题目选讲

4.5 注解与习题

4.5.1 头文件、副作用及其他

第5章 C++与STL入门

5.1 从C到C++

5.2 STL初步

5.2.1 排序与检索
5.2.2 不定长数组:vector
5.2.3 集合:set
5.2.4 映射:map
5.2.5 栈、队列与优先队列
5.2.6 测试STL

5.3 应用:大整数类

5.3.1 大整数类BigInteger
5.3.2 四则运算
5.3.3 比较运算符

5.4 竞赛题目选讲

5.5 习题

第2部分 基础篇

第6章 数据结构基础

6.1 再谈栈和队列

6.2 链表

6.3 树和二叉树

6.3.1 二叉树的编号
6.3.2 二叉树的层次遍历
6.3.3 二叉树的递归遍历
6.3.4 非二叉树

6.4 图

6.4.1 用DFS求连通块
6.4.2 用BFS求最短路
6.4.3 拓扑排序
6.4.4 欧拉回路

6.5 竞赛题目选讲

6.6 训练参考

第7章 暴力求解法

7.1 简单枚举

7.2 枚举排列

7.2.1 生成1~n的排列
7.2.2 生成可重集的排列
7.2.3 解答树
7.2.4 下一个排列

7.3 子集生成

7.3.1 增量构造法
7.3.2 位向量法
7.3.3 二进制法

7.4 回溯法

7.4.1 八皇后问题
7.4.2 其他应用举例

7.5 路径寻找问题

7.6 迭代加深搜索

7.7 竞赛题目选讲

7.8 训练参考

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值