算法初步笔记之分支结构程序设计

算法笔记(三)


3.1 关系表达式与逻辑表达式

计算机的世界由 0 和 1 组成,0 代表假,1 代表真。

一个简单的例子:输入两个整数 a a a b b b,想知道:
1) a a a 是否大于 b b b
2) a a a 是否小于或等于 b b b
3) a a a 是否不等于 b b b

#include<iostream>
using namespace std;
int main (){
	int a, b;
	cin >> a >> b;
	cout << (a > b) << ' ';
	cout << (a <= b) << ' ';
	cout << (a != b) << endl;
	return 0;
}

输入

输出

注意,如果要比较两个数是否相等,使用的是 == 而不是 =,因为一个等号是赋值的意思。有这些关系运算符组成的不是 0 就是 1 的表达式就是关系表达式。
运算符优先级
括号的优先级最高,所以计算机会先去计算括号里面的内容,其次是乘除取余,然后是加减。而大于或小于之类的关系运算符优先级还要低一些,相等或不等的优先级更低。
特殊地,一般不会用 == 来判断两个浮点数是否相等,这是因为浮点数可能会产生精度误差。正确的方式是比较这两个数的差值是否小于一定程度。例如,假设 fabs(a - b) < 1e-6成立,就可以认为浮点数 a 和 b 相等。

例题一 数的性质(洛谷P5710)
一些整数可能拥有以下的性质:

  • 性质 1:是偶数;
  • 性质 2:大于 4 4 4 且不大于 12 12 12

小 A 喜欢这两个性质同时成立的整数;Uim 喜欢这至少符合其中一种性质的整数;小 B 喜欢刚好有符合其中一个性质的整数;正妹喜欢不符合这两个性质的整数。现在给出一个整数 x x x,请问他们是否喜欢这个整数?
输入一个整数 x ( 0 ≤ x ≤ 1000 ) x(0\le x \le 1000) x(0x1000)
输出这 4 4 4 个人是否喜欢这个数字,如果喜欢则输出 1,否则输出 0,用空格分隔。输出顺序为:小 A、Uim、小 B、正妹。

样例输入

12

样例输出

1 1 0 0
#include<iostream>
using namespace std;
int main (){
	int x;
	bool p1, p2;
	cin >> x;
	p1 = x % 2 == 0;
	p2 = 4 < x && x <= 12;
	cout << (p1 && p2) << ' '; // 两个性质同时成立
	cout << (p1 || p2) << ' '; // 两个性质至少一个成立
	cout << (p1 ^ p2) << ' '; // 两个性质刚好一个成立
	cout << (!p1 && !p2); // 两个性质同时不成立
	// cout << !(p1 || p2); // 也可以这样写
	return 0;
}

分析:如果需要像题目这样将多个条件复合成一个条件进行判断,在汉语语境下类似“且”“或”“不”的情况,就需要使用逻辑运算符进行连接,这样的表达式称为逻辑表达式。和关系表达式一样,逻辑表达式也是取值 0 或 1。
值得注意的是,此处为了简化问题,将性质 1 和性质 2 存成 bool 类型的变量,其占用 1 字节,只能表示 0 或 1 这两种取值。
在 C++ 中可以使用以下几种逻辑运算符。
1)与运算符:用 && 来判断两个条件是否同时成立。
2)或运算符:用 || 来判断两个条件是否至少有一个成立。
3)异或运算符:用 ^ 表示两个条件是否刚好一个成立、另一个不成立。
4)非运算符:用 !可以将一个条件取反(将其颠倒)。
逻辑运算产生的结果也是 0 (不成立) 或者 1 (成立)。
逻辑运算规则
对于 x%2==0 || 4<x&&x<=12,可以通过加括号使条件变得清晰,也就是 (x % 2 == 0) || ((4 < x) && (x <= 12))。
运算符优先级补充

例题二 闰年判断(洛谷P5711)。输入一个年份(大于1582的整数),判断这一年是否为闰年,如果是,输出 1;否则,输出 0。
分析:被 4 整除是闰年,但被 100 整除不是闰年;而被 400 整除又是闰年。

#include<iostream>
using namespace std;
int main (){
	int x;
	bool p;
	cin >> x;
	p = (x % 400 == 0) || (x % 4 == 0) && (x % 100 != 0);
	// p = !(x % 400) || !(x % 4) && x % 100; // 也可这样写
	cout << p <<endl;
	return 0;
}

对于 &&、|| 和 ! 来说,参与计算的元素如果非零,则无论是正数还是负数,都会被视为 1。
对于 x 是不是 正常数 a 的 倍数,只需判断 x % a 与 0 的关系,若需要 x 不是 a 的倍数,则可将 x % a != 0 简化为 x % a,这是因为此时 x % a 非零,为 1;反之,需要 x 是 a 的倍数,则 x % a == 0 与 !(x % a) 等价。

3.2 分支语句

例题一 Apples(P5712)。小 B 喜欢吃苹果。她今天吃掉了 x x x 个苹果。英语课上学到了 apple 这个词语,想用它来造句。如果她吃了 1 个苹果,就输出 Today, I ate 1 apple.;如果她没有吃,那么就把 1 换成 0;如果她吃了不止一个苹果,别忘了 apple 这个单词后面要加上代表复数的 s。你能帮她完成这个句子吗?输入一行一个自然数 x x x,表示吃掉的苹果数。对于所有数据, 0 ≤ x ≤ 100 0\le x \le 100 0x100

#include<iostream>
using namespace std;
int main (){
	int x;
	cin >> x;
	cout << "Today, I ate " << x << " apple";
	if (x != 0 && x != 1){ // 也可写成 !(x == 0 || x == 1)
		cout << "s";		
	}
	cout << "." << endl;  
	return 0;
}

if 语句的一种用法如下:
if (成立条件表达式){ 当条件成立时需要执行的语句; }

例题二:洛谷团队系统(洛谷P5713)在洛谷上使用团队系统非常方便的添加自己的题目。如果在自己的电脑上配置题目和测试数据,每题需要花费时间 5 5 5 分钟;而在洛谷团队中上传私有题目,每题只需要花费 3 3 3 分钟,但是上传题目之前还需要一次性花费 11 11 11 分钟创建与配置团队。现在要配置 n n n 道题目,如果本地配置花费的总时间短,请输出 Local,否则输出 Luogu。输入一个正整数 n n n,表示需要配置的题目量。输出一行,一个字符串。如果本地配置花费的总时间短,请输出 Local,否则输出 Luogu。数据保证 1 ≤ n ≤ 100 1 \leq n\leq 100 1n100

#include<iostream>
using namespace std;
int main (){
	int n;
	cin >> n;
	if ((5 * n) < (11 + 3 * n)){
		cout << "Local" << endl;
	} else{
		cout << "Luogu" << endl;
	}
	return 0;
}

if-else语句:
if(成立条件表达式){
当条件成立时需要执行的语句;
} else {
当条件不成立时需要执行的语句;
}
使用大括号括起来的语句称为代码块。特别地,如果需要执行的语句只有一条语句,那么可以省略大括号。

例题二 小洛机器人。输入不同的字符指令,可以得到不同的回复。

#include<iostream>
using namespace std;
int main (){
	char opt;
	cin >> opt;
	switch (opt){
		case 'G': cout << "Hello!" << endl;
		case 'N': cout << "I'm Xiaoluo." << endl; break;
		case 'S': cout << "Sing." << endl; break;
		case 'B': case 'Q':  
			cout << "Bye bye!" << endl;
			break;
		default: cout << "Sorry.." << endl;
	}
	return 0;
}

switch-case 语句:
switch (变量名){
case 变量可能的情况1: 执行语句1; break;
case 变量可能的情况2: 执行语句2; break;

default : 执行语句 n;
}
break; 语句是使程序跳出switch-case 语句。需要注意的是,如果某种情况运行完后没有break; 语句,程序就会继续执行下一种情况的语句,直到遇到break; 语句或执行完所有语句。如果一开始就没有匹配到任何一种情况,那么就会运行 default 后面的语句。
switch-case 语句的 case 需要的只能是常量,不能是变量。这些常量类型可以是整数,也可以是字符类型等,但不能是浮点数。对于浮点数,则还是要用多重 if 嵌套作为分支判断,且最好不要直接用 == 判断浮点数相等。

3.3 分支嵌套

例题一 肥胖问题(洛谷P5714)。BMI 指数是国际上常用的衡量人体胖瘦程度的一个标准,其算法是 m h 2 \dfrac{m}{h^2} h2m,其中 m m m 是指体重(千克), h h h 是指身高(米)。不同体型范围与判定结果如下:

  • 小于 18.5 18.5 18.5:体重过轻,输出 Underweight
  • 大于等于 18.5 18.5 18.5 且小于 24 24 24:正常体重,输出 Normal
  • 大于等于 24 24 24:肥胖,不仅要输出 BMI 值(使用 cout 的默认精度),然后换行,还要输出 Overweight

现在给出体重和身高数据,需要根据 BMI 指数判断体型状态并输出对应的判断。输入共 2 2 2 个浮点数, m , h m, h m,h,分别表示体重(单位为 kg),身高(单位为 m)。输出一行一个字符串,表示根据 BMI 的对应判断。特别地,对于 Overweight 情况的特别处理请参照题目所述。对于所有数据, 40 ≤ m ≤ 120 40\le m \le 120 40m120 1.4 ≤ h ≤ 2.0 1.4 \le h \le 2.0 1.4h2.0 m m m h h h 的小数点后不超过三位。

#include<iostream>
using namespace std;
int main (){
	double m, h, BMI;
	cin >> m >> h;
	BMI = m / h / h;
	if(BMI < 18.5)
		cout << "Underweight";
	else if (BMI < 24)
		cout << "Normal";
	else{
		cout << BMI <<endl;
		cout << "Overweight";
	}
	return 0;	
}

例题二 三个数排序(洛谷P5715)。给出三个整数 a , b , c ( 0 ≤ a , b , c ≤ 100 ) a,b,c(0\le a,b,c \le 100) a,b,c(0a,b,c100),要求把这三位整数从小到大排序。输入三个整数 a , b , c a,b,c a,b,c,以空格隔开。输出一行,三个整数,表示从小到大排序后的结果。

解法1:枚举所有的情况,注意存在有数相等的情况

#include<cstdio>
using namespace std;
int main (){
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	if(a <= b && b <= c)
		printf("%d %d %d", a, b, c);
	else if(a <= c && c <= b)
		printf("%d %d %d", a, c, b);
	else if(b <= a && a <= c)
		printf("%d %d %d", b, a, c);
	else if(b <= c && c <= a)
		printf("%d %d %d", b, c, a);
	else if(c <= a && a <= b)
		printf("%d %d %d", c, a, b);
	else printf("%d %d %d", c, b, a);	
	return 0;
}

解法2:先找出最小的数,后枚举

#include<cstdio>
using namespace std;
int main (){
	int a, b, c;
	scanf("%d%d%d", &a, &b, &c);
	if(a <= b && a <= c){
		if(b <= c) printf("%d %d %d", a, b, c);
		else printf("%d %d %d", a, c, b);
	}
	else if(b <= a && b <= c){
		if(a <= c) printf("%d %d %d", b, a, c);
		else printf("%d %d %d", b, c, a);
	}
	else{
		if(a <= b) printf("%d %d %d", c, a, b);
		else printf("%d %d %d", c, b, a);
	}
	return 0;
}

解法三:是我之前用C语言写的,由于只有三个数,找出最大值,最小值后,中间值用三个数的和减去最大值和最小值即可。

#include<stdio.h>
int main (){
	int a, b, c;
	scanf("%d %d %d", &a, &b, &c);
	int max, min;
	max = a > b ? a : b;
	min = a < b ? a : b;
	max = max > c ? max : c;
	min = min < c ? min : c;
	printf("%d %d %d", min, a+b+c-max-min, max);
    return 0;
}

例题三 月份天数(洛谷P5716)。输入年份和月份,输出这一年的这一月有多少天。需要考虑闰年。输入两个正整数,分别表示年份 y y y 和月数 m m m,以空格隔开。输出一行一个正整数,表示这个月有多少天。数据保证 1583 ≤ y ≤ 2020 1583 \leq y \leq 2020 1583y2020 1 ≤ m ≤ 12 1 \leq m \leq 12 1m12

#include<iostream>
using namespace std;
int main (){
	int y, m;
	cin >> y >> m;
	switch (m){
		case 1: case 3: case 5: case 7: case 8: case 10: case 12:
			cout << 31; 
			break;
		case 4: case 6: case 9: case 11:
			cout << 30; 
			break;
		case 2:
			if(!(y % 400) || !(y % 4) && y % 100)
				cout << 29;
			else cout << 28;
	}
	return 0;
}

3.4 分支程序设计案例

例题一 不高兴的津津(洛谷P1085,NOIP2004 普及组)。津津上初中了。妈妈认为津津应该更加用功学习,所以津津除了上学之外,还要参加妈妈为她报名的各科复习班。另外每周妈妈还会送她去学习朗诵、舞蹈和钢琴。但是津津如果一天上课超过八个小时就会不高兴,而且上得越久就会越不高兴。假设津津不会因为其它事不高兴,并且她的不高兴不会持续到第二天。请你帮忙检查一下津津下周的日程安排,看看下周她会不会不高兴;如果会的话,哪天最不高兴。
输入格式
输入包括 7 7 7 行数据,分别表示周一到周日的日程安排。每行包括两个小于 10 10 10 的非负整数,用空格隔开,分别表示津津在学校上课的时间和妈妈安排她上课的时间。
输出格式
一个数字。如果不会不高兴则输出 0 0 0,如果会则输出最不高兴的是周几(用 1 , 2 , 3 , 4 , 5 , 6 , 7 1, 2, 3, 4, 5, 6, 7 1,2,3,4,5,6,7 分别表示周一,周二,周三,周四,周五,周六,周日)。如果有两天或两天以上不高兴的程度相当,则输出时间最靠前的一天。

#include<iostream>
using namespace std;
int main (){
	int t1, t2, maxtime = 8, maxday = 0;
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 1;} 
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 2;}
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 3;}
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 4;}
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 5;}
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 6;}
	cin >> t1 >> t2;
	if(t1 + t2 > maxtime){ maxtime = t1 + t2; maxday = 7;}
	cout << maxday;
	return 0;
}

例题二 买铅笔(洛谷P1909,NOIP2016 普及组)。P 老师需要去商店买 n n n 支铅笔作为小朋友们参加 NOIP 的礼物。她发现商店一共有 3 3 3 种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P 老师决定只买同一种包装的铅笔。商店不允许将铅笔的包装拆开,因此 P 老师可能需要购买超过 n n n 支铅笔才够给小朋友们发礼物。现在 P 老师想知道,在商店每种包装的数量都足够的情况下,要买够至少 n n n 支铅笔最少需要花费多少钱。
输入格式
第一行包含一个正整数 n n n,表示需要的铅笔数量。
接下来三行,每行用 2 2 2 个正整数描述一种包装的铅笔:其中第 1 1 1 个整数表示这种包装内铅笔的数量,第 2 2 2 个整数表示这种包装的价格。
保证所有的 7 7 7 个数都是不超过 10000 10000 10000 的正整数。
输出格式
1 1 1 个整数,表示 P 老师最少需要花费的钱。

#include<iostream>
using namespace std;
int main (){
	int n, n1, p1, n2, p2, n3, p3, m1, m2, m3, min;
	cin >> n >> n1 >> p1 >> n2 >> p2 >> n3 >> p3;
	m1 = !(n % n1) ? n / n1 * p1 : (n / n1 + 1) * p1;
	m2 = !(n % n2) ? n / n2 * p2 : (n / n2 + 1) * p2;
	m3 = !(n % n3) ? n / n3 * p3 : (n / n3 + 1) * p3;
	min = m1 < m2 ? m1 : m2;
	min = min < m3 ? min : m3;
	cout << min;
	return 0;
}

这里使用了问号表达式,其形式为S1 ? S2 : S3,意思是如果 S1 条件成立,那么它的值就是 S2,否则是S3。
本题还可以用 cmath 头文件中的 ceil() 函数,进行向上取整运算。

例题三 ISBN 号码(洛谷P1055, NOIP2008 普及组)。每一本正式出版的图书都有一个 ISBN 号码与之对应,ISBN 码包括 9 9 9 位数字、 1 1 1 位识别码和 3 3 3 位分隔符,其规定格式如 x-xxx-xxxxx-x,其中符号 - 就是分隔符(键盘上的减号),最后一位是识别码,例如 0-670-82162-4就是一个标准的 ISBN 码。ISBN 码的首位数字表示书籍的出版语言,例如 0 0 0 代表英语;第一个分隔符 - 之后的三位数字代表出版社,例如 670 670 670 代表维京出版社;第二个分隔符后的五位数字代表该书在该出版社的编号;最后一位为识别码。
识别码的计算方法如下:
首位数字乘以 1 1 1 加上次位数字乘以 2 2 2 ……以此类推,用所得的结果   m o d   11 \bmod 11 mod11,所得的余数即为识别码,如果余数为 10 10 10,则识别码为大写字母 X X X。例如 ISBN 号码 0-670-82162-4 中的识别码 4 4 4 是这样得到的:对 067082162 9 9 9 个数字,从左至右,分别乘以 1 , 2 , … , 9 1,2,\dots,9 1,2,,9 再求和,即 0 × 1 + 6 × 2 + … … + 2 × 9 = 158 0\times 1+6\times 2+……+2\times 9=158 0×1+6×2+……+2×9=158,然后取 158   m o d   11 158 \bmod 11 158mod11 的结果 4 4 4 作为识别码。
你的任务是编写程序判断输入的 ISBN 号码中识别码是否正确,如果正确,则仅输出 Right;如果错误,则输出你认为是正确的 ISBN 号码。
输入格式
一个字符序列,表示一本书的 ISBN 号码(保证输入符合 ISBN 号码的格式要求)。
输出格式
一行,假如输入的 ISBN 号码的识别码正确,那么输出 Right,否则,按照规定的格式,输出正确的 ISBN 号码(包括分隔符 -)。

#include<cstdio>
using namespace std;
int main (){
	char a, b, c, d, e, f, g, h, i, j;
	int check;
	scanf("%c-%c%c%c-%c%c%c%c%c-%c", &a, &b, &c, &d, &e, &f, &g, &h, &i, &j);
	check = ((a - '0') * 1 + (b - '0') * 2 + (c - '0') * 3 + (d - '0') * 4 + (e - '0') * 5 + (f - '0') * 6 + (g - '0') * 7 + (h - '0') * 8 + (i - '0') * 9) % 11;
	if((check == 10 && j == 'X')||(check == j - '0')) 
		printf("Right");
	else
		printf("%c-%c%c%c-%c%c%c%c%c-%c", a, b, c, d, e, f, g, h, i, check == 10 ? 'X' : check + '0');
	return 0;
}
  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值