第 3 章 c++ 中的条件与循环

68
1 第 3 第 C++ 第第第第第第第 第 3 第第第acm.nefu.edu.cn/C+ +_03.ppt

Upload: kane

Post on 20-Jan-2016

170 views

Category:

Documents


0 download

DESCRIPTION

第 3 章 C++ 中的条件与循环. 第 3 次见面! acm.nefu.edu.cn/C++_03.ppt. 这 2 天你们 C++ 吗?. 一夜北风寒,万里彤云厚; 长空雪乱飘,改尽江山旧。 仰面观太虚,疑是玉龙斗; 纷纷鳞甲飞,顷刻遍宇宙。 骑驴过小桥,独叹梅花瘦! 谁知道出自 ------ ? 是 李白 吗?. 上次实验课总结. 1 、注意汉字和英文的输入法; 2 、 endl 不是 end1; 3 、时时刻刻使用 C++ 的基本框架. - PowerPoint PPT Presentation

TRANSCRIPT

1

第 3 章 C++ 中的条件与循环

第 3 次见面! acm.nefu.edu.cn/C++_03.ppt

2

这 2 天你们 C++ 吗?

• 一夜北风寒,万里彤云厚;• 长空雪乱飘,改尽江山旧。• 仰面观太虚,疑是玉龙斗;• 纷纷鳞甲飞,顷刻遍宇宙。• 骑驴过小桥,独叹梅花瘦! • 谁知道出自 ------ ?• 是李白吗?

上次实验课总结• 1 、注意汉字和英文的输入法;• 2、 endl 不是 end1;

• 3 、时时刻刻使用 C++ 的基本框架

3

上机调试的心得(授之以渔)• 1 、哪行出错了• 2 、提示的字符是啥• 3 、是不是自己敲错了• 4 、缺头文件吗? was not declared in this

scope

• 5 、结构,大括号,大小写• 6 、然后才是真的逻辑出错了

4

先学习 C++ 实验的在线评测系统

• 记住网址:• acm.nefu.edu.cn

5

acm.nefu.edu.cn

6

注册 register

7

Problem 题目

8

如何做题• 先看题,如 “寻找吕布”这题;• description

• 三国里面吕布第一,赵云第二,典韦、关羽和马超分别是第 3、第 4和第 5名,这是按武将的勇猛值和必杀技值的和来排名的,即武术值 =勇猛值 +必杀技值,下面给出这 5人的勇猛值和必杀值,请你找出吕布的武术值。

9

寻找吕布• input

• 输入数据有多组,每组数据 2行,第一行是 5人的勇猛值,第二行是 5人的必杀技值。勇猛值和必杀值是整数哦( 32

位) output

• 输出吕布的武术值。

10

• sample_input

• 1 2 3 4 5

• 1 2 3 4 5

• 20 21 22 45 87

• 1 100 8 99000 23

• sample_output

• 10

• 99087

• hint

• 吕布的勇猛值和必杀技值都是第一的! 11

• #include <iostream>

• using namespace std;

• int main()

• {

• int data[6],inp[6];// 定义变量• int ym,bs;// 勇猛值 必杀值的变量• while(cin>>data[1]>>data[2]>>data[3]>>data[4]>>data[5])// 注意• {

• ym=0,bs=0; // 这句很重要,必须先清 0

• for(int i=1;i<=5;i++) if (ym<data[i]) ym=data[i];

• cin>>inp[1]>>inp[2]>>inp[3]>>inp[4]>>inp[5];

• for(int i=1;i<=5;i++) if (bs<inp[i]) bs=inp[i];

• cout<<ym+bs<<endl;

• }

• //cout << “Hello world!” << endl; 记得要注视掉啊• return 0;

• }12

再看服务器里的数据• data.in 输入文件 data.out 输出文件• 1 2 3 4 5 10

• 1 2 3 4 5 99087

• 20 21 22 45 87 16

• 1 100 8 99000 23

• 1 2 6 8 9

• 7 4 3 2 1 你不知道服务器的数据是啥! 13

提交答案 : 题目下面 submit

14

看评判结果

15

欢迎有敏感的人加入 ACM

• 数学好• 算法好(可以培养)• 能吃苦就行

16

条件语句• if

• switch

• 和 C 语言一样,基本知识不变• 通过实例来讲解

17

条件表达式• ?:运算符 :问号冒号运算符• 作用:更加简练的用来表达条件执行的方式 • 形式 : ( 条件 ) ? 表达式 1 : 表达式 2

• 执行过程:首先计算条件值。如果条件结果为true ,则计算表达式 1 的值,并将它作为整个表达式的值。如果条件结果为 false ,则整个表达式的值为表达式 2 的值。

实例• 例如将 x和 y 中值较大的一个赋值给 max ,可以用下列语句:

max = (x > y) ? x : y;

• ?:运算符用于输出。例如,想输出一个布尔变量 flag 的值,如果直接用 cout << flag;

那么当 flag 为“真”时,输出为 1 ;当 flag 为“假”时,输出为 0 。

如果我们想让 flag 为“真”时输出 true ,为“假”时输出false ,可以用 if 语句 if (flag) cout << “true”;

else cout << “false”;

看上去太罗嗦。但如果用?:运算符只需要一行 cout << ( flag ? "true" : "false" ) << endl;

判断闰年的程序#include <iostream>using namespace std;

int main(){ int year; bool result;

cout << " 请输入所要验证的年份: "; cin >> year;

result = (year % 4 == 0 && year % 100 !=0)|| year % 400 == 0;

if (result) cout << year << " 是闰年 " << endl; else cout << year << " 不是闰年 " << endl;

return 0; }

计算机自动出四则运算计算题

生成题目Switch( 题目类型 )

{ case 加法:显示题目,输入和的值,判断正确与否 case 减法:显示题目,输入差的值,判断正确与否 case 乘法:显示题目,输入积的值,判断正确与否 case 除法:显示题目,输入商和余数的值,判断正确与否}

要求自动出 0 - 9 之间的四则运算题,并批改结果

关键问题• 如何让程序每次执行的时候都出不同的题目?

• 随机数生成器 rand() :能随机生成 0到

RAND_MAX 之间的整型数

• 将生成的随机数映射到 0 - 9 之间:– Rand() % 10

– rand() * 10 / (RAND_MAX + 1) 。

• 运算符的生成:用编码 0 - 3 表示四个运算符。因此题目的生成就是生成 0 - 3 之间的随机数。

随机数的种子• 计算机产生的随机数称为伪随机数,它是根据一个

算法计算出来的。

• 系统为每个程序、每次执行指定的随机数的种子都是相同的,因此程序每次执行生成的随机数序列都是相同的。

rand()

种子

12348

12348

改变随机数的种子

• 设置种子的函数 srand : srand (种子)

• 如何让程序每次执行时选择的种子都不一样呢 ?

• 选择系统时间为种子: time(NULL) 取当前的系统时间。

#include <cstdlib> // 包含伪随机数生成函数#include <ctime> // 包含取系统时间的函数#include <iostream>

using namespace std;

int main()

{

int num1, num2, op, result1, result2;

//num1,num2: 操作数, op: 运算符, result1,result2: 结果

srand(time(NULL)); // 随机数种子初始化

num1=rand() * 10 / (RAND_MAX + 1); // 生成运算数 RAND_MAX=32767

num2=rand() * 10 / (RAND_MAX + 1); // 生成运算数 op=rand() * 4 / (RAND_MAX + 1);

// 生成运算符 0--+ , 1-- - , 2--*, 3-- /

自动出题程序

switch (op)

{case 0: cout << num1 << "+" << num2 << "= ?" ; cin >> result1;

if (num1 + num2 == result1) cout << "you are right\n";

else cout << "you are wrong\n";

break;

case 1: cout << num1 << "-" << num2 << "= ?" ; cin >> result1;

if (num1 - num2 == result1) cout << "you are right\n";

else cout << "you are wrong\n";

break;

case 2: cout << num1 << "*" << num2 << "= ?" ; cin >> result1;

if (num1 * num2 == result1) cout << "you are right\n";

else cout << "you are wrong\n";

break;

case 3: cout << num1 << "/" << num2 << "= ?" ;

cin >> result1;

cout << “ 余数为 = ?"; cin >> result2;

if ((num1 / num2 == result1)

&& (num1 % num2 == result2))

cout << "you are right\n";

else cout << "you are wrong\n";

break;

}

return 0;

}

该程序的缺陷

• 每次执行只能出一道题• 减法可能出现负值• 除法可能出现除 0

• 结果太单调

再看循环结构• For 基本的习惯• While 用处• Do while

29

For 循环实例

• 某班级有 100 个学生,设计一程序统计该班级某门考试成绩中的最高分、最低分和平均分。

• 方案一:先输入 100 个整型数,保存在各自的变量中。然后依次检查这 100 个数,找出最大的和最小的。在找的过程中顺便可以把所有的数都加起来。最后将总和除100 就得到了平均值。

方案一的缺陷• 需要定义 100 个变量• 需要输入 100 个变量的值• 从 100 个变量中找出最大者,需要 100个 if 语句

• 从 100 个变量中找出最小者,需要 100个 if 语句

• 将这 100 个变量加起来需要一个长长的算术表达式

方案二

• 每个学生的分数在处理过后就没用了,为此,可以用一个变量保存当前正在处理的分数

• 每次输入分数的同时将它们加起来: 70

加 40等于 110, 110加 80等于 190……

。并记住最低分的和最高分的值。上述过程重复 100 次。

方案二的实现

• 定义: int value, total, max, min;

• 当输入每个数值时必须执行下面的步骤,这可以用 for循环实现– 请求用户输入一个整数值,将它存储在变量 value

中。– 将 value 加入到保存当前和的变量 total 中。– 如果 value 大于 max ,将 value存于 max 。– 如果 value 小于 min ,将 value存于 min 。

#include<iostream>using namespace std;

int main(){ int value, total, max, min, i;//value :当前输入数据, i 为循环变量

total = 0; max = 0; min = 100; // 变量的初始化

for (i=1; i<=100; ++i) { cout << "\n 请输入第 " << i << " 个人的成绩: "; cin >> value;

total += value; if (value > max) max = value; if (value < min) min = value;

}

cout << "\n 最高分: " << max << endl; cout << " 最低分: " << min << endl;

cout << " 平均分: " << total / 100 << endl;

return 0;}

注意缩进

For 循环实例• 求函数 在区间 [a, b] 之间的定积分• 实现思想:函数与 x轴围成的区域的面积。定积分

可以通过将这块面积分解成一连串的小矩形,计算各小矩形的面积的和而得到

15)( 2 xxxf

15)( 2 xxxf

a b

int main(){

double a, b, dlt, integral = 0;

cout << " 请输入定积分的区间: " ;cin >> a >> b;

cout << " 请输入小矩形的宽度: " ; cin >> dlt;

for (double x = a + dlt / 2; x < b; x += dlt) integral += (x * x + 5 * x + 1) * dlt;

cout << "积分值为: " << integral << endl;

return 0;}

eg 2. 求 时结束。 000001.0!!

...!3!2

132

n

x

n

xxxxe

nnx

ex=0;

p = 1;

while (p>0.000001) {

ex += p;

计算新的 p ; }

问题:

如何计算 p ?计算第 i个 p ,需要两个 i 次的循环。第一个循环计算 xi ,第二个循环计

算 i !

解决方案:

从前一项计算后一项。如果 p是第 i项的值,则第 i+1项的

值为 p*x/(i+1)

int main(){double ex, x, p;//ex 存储 ex 的值, p 保存当前项的值

int i;

cout << " 请输入 x : "; cin >> x;

ex=0; p=1; i=0;

while (p > 1e-6){ ex += p;

++i; p = p * x / i;

}

cout << "e的 " << x << " 次方等于: " << ex << endl;

return 0;}

枚举法

• 对所有可能的情况一种一种去尝试,直到找到正确的答案。

• 枚举法的实现基础是循环。

枚举法实例一• 用 50元钱买了三种水果。各种水果加起来一共 100 个。西瓜 5元一个,苹果 1元一个,桔子 1元 3 个,设计一程序输出每种水果各买了几个

• 它有两个约束条件:– 第一是三种水果一共 100 个;– 第二是三种水果一共花了 50元

• 可以按一个约束条件列出所有可行的情况,然后对每个可能解检查它是否满足第二个约束条件 。也可以用第二个约束条件列出所有情况,然后对每个可能解检查它是否满足第一个约束条件 。

#include <iostream>using namespace std;

int main() { int mellon, apple, orange; // 分别表示西瓜数、苹果数和桔子数 for (mellon=1; mellon<10; ++mellon) // 对每种可能的西瓜数 for ( apple=1; apple < 50 - 5 * mellon; ++apple) { // 当西瓜数给定后可能的苹果数 orange = 3*(50-5*mellon-apple); // 剩下的钱全买了桔子 if (mellon+apple+orange == 100){ // 三种水果数之和是否为100 cout << "mellon:" << mellon << ' '; cout << "apple:" << apple << ' ';

cout << "orange:" << orange << endl; }

} return 0; }

执行结果

Mellon : 1 apple : 18 orange :81

Mellon : 2 apple : 11 orange :87

Mellon: 3 apple: 4 orange:93

实例二 — 四大湖问题

上地理课时,四个学生回答我国四大湖的大小时分别说:

甲:洞庭最大,洪泽最小,鄱阳第三 乙:洪泽最大,洞庭最小,鄱阳第二,太湖第三 丙:洪泽最小,洞庭第三 丁:鄱阳最大,太湖最小,洪泽第二,洞庭第三对于每个湖的大小,每个人仅答对一个,设计一程序让

计算机通过这些信息去判别四个湖的大小。

解题思路

• 如果用 a,b,c,d分别表示四个湖的排序。 a 表示洞庭湖, b 表示洪泽湖, c 表示鄱阳湖, d 表示太湖。我们可以假设:洞庭最大,洪泽第二,鄱阳第三,太湖第四,然后检查每位同学是否都讲对了一个。如果不是,再尝试下一种情况:洞庭最大,洪泽第二,鄱阳第四,太湖第三,再检查每位同学是否都讲对了一个。尝试所有可能的情况,直到满足每位同学都讲对一个为止。

枚举法—续

• 为了尝试所有情况,我们需要假设洞庭湖可能是最大,也可能是第二、第三或第四。因此, a 的值可能从 1 变到 4 。同样, b, c ,d 的值也都可能从 1 变到 4。为此,我们需要一个控制结构,使 a, b, c, d 的值能自动从 1 变到 4 。这种结构就是循环结构。

四大湖排列问题的解main(){ int a, b, c, d; for (a=1; a<=4; ++a) for (b=1; b<=4; ++b) if ( a == b) continue; else for (c=1; c<=4; ++c) if (c==a||c==b) continue; else {d=10 – a – b - c;

if (((a==1)+(b==4)+(c==3))==1 &&((b==1)+(a==4)+(c==2)+(d==3))==1

&&((b==4)+(a==3))==1 &&((c==1)+(d==4)+(b==2)+(a==3))==1)

cout << a << b << c << d;}

}

问题:效率差

解决方法:一旦找到答案就应该结束

main(){ int a, b, c, d; bool flag = false; for (a=1; a<=4; ++a) { for (b=1; b<=4; ++b) { if ( a == b) continue; else for (c=1; c<=4; ++c) if (c==a||c==b) continue; else {d=10 – a – b - c;

if (((a==1)+(b==4)+(c==3))==1 // 注意 &&((b==1)+(a==4)+(c==2)+(d==3))==1

&&((b==4)+(a==3))==1 &&((c==1)+(d==4)+(b==2)+(a==3))==1)

{ cout << a << b << c << d; flag = true; break; }

} if (flag) break;} if (flag) break;}} //break 只能退出当前循环体

改进版 1 :

程序不够简练

main(){ int a, b, c, d; bool flag = false; for (a=1; a<=4 && !flag; ++a) { for (b=1; b<=4 && !flag; ++b) { if ( a == b) continue; else for (c=1; c<=4 ; ++c) if (c==a||c==b) continue; else {d=10 – a – b - c;

if (((a==1)+(b==4)+(c==3))==1 &&((b==1)+(a==4)+(c==2)+(d==3))==1

&&((b==4)+(a==3))==1 &&((c==1)+(d==4)+(b==2)+(a==3))==1)

{ cout << a << b << c << d; flag = true; break; }

} } }}

改进版 2

列出 ABC 三个字母的全排列

• 解题思路:– 让第一个位置的值从 A依次变到 C

– 让第一个位置的值从 A依次变到 C

– 让第一个位置的值从 A依次变到 C

– 注意三个位置的值不能相同

• 可以用一个三层的嵌套循环实现,循环变量是字符类型

int main()

{ char c1, c2, c3;

for (c1 = ‘A’; c1 <= ‘C’; ++c1)

for (c2 = ‘A’; c2 <= ‘C’; ++c2)

if (c1 == c2) continue;

else for (c3 = ‘A’; c3 <= ‘C’; ++c3)

if ((c3 == c1 || c3 == c2))

continue;

else cout << c1 << c2 << c3 << endl;

}

贪婪法的基本思想• 在求解过程的每一步都选取一个局部最优

的策略,把问题规模缩小,最后把每一步的结果合并起来形成一个全局解。

• 基本步骤:– 从某个初始解出发– 采用迭代的过程,当可以向目标前进一步时,

就根据局最优策略,得到一个部分解,缩小问题规模。

– 将所有解综合起来

硬币找零问题

• 对于一种货币,有面值为 1分 , 2分 , 5

分和 1角的硬币,最少需要多少个硬币来找出 K分钱的零钱。

贪婪法解题思想

• 不断地使用面值最大的硬币。如要找零的值小于最大的硬币值,则尝试第二大的硬币。依此类推。

• 不断尝试的过程就是循环

#include<iostream>using namespace std;

#define ONEFEN 1#define TWOFEN 2#define FIVEFEN 5#define ONEJIAO 10

int main(){ int money; int onefen = 0, twofen = 0, fivefen = 0, onejiao = 0; cout << " 输入要找零的钱(以分为单位): "; cin >> money;

// 不断尝试每一种硬币 while (money >= ONEJIAO) {onejiao++; money -= ONEJIAO;} while (money >= FIVEFEN) {fivefen++; money -= FIVEFEN;} while (money >= TWOFEN) {twofen++; money -= TWOFEN;} while (money >= ONEFEN) {onefen++; money -= ONEFEN;}

// 输出结果 cout << "1 角硬币数: " << onejiao << endl; cout << "5 分硬币数: " << fivefen << endl; cout << "2 分硬币数: " << twofen << endl; cout << "1 分硬币数: " << onefen << endl; return 0;}

23/4/21 56

例:用贪心法求解付款问题。假设有面值为 5 元、 2 元、 1 元、 5 角、 2 角、 1 角的货币,需要找给顾客 4元 6 角现金,为使付出的货币的数量最少,首先选出 1 张面值不超过 4元 6 角的最大面值的货币,即 2 元,再选出 1 张面值不超过 2元 6 角的最大面值的货币,即 2 元,再选出 1 张面值不超过 6 角的最大面值的货币,即 5 角,再选出 1 张面值不超过 1 角的最大面值的货币,即 1 角,总共付出 4 张货币。

23/4/21 57

在付款问题每一步的贪心选择中,在不超过应付款金额的条件下,只选择面值最大的货币,而不去考虑在后面看来这种选择是否合理,而且它还不会改变决定:一旦选出了一张货币,就永远选定。付款问题的贪心选择策略是尽可能使付出的货币最快地满足支付要求,其目的是使付出的货币张数最慢地增加,这正体现了贪心法的设计思想。

23/4/21 58

思考题

•Nefu 90题 今年暑假不 AC

23/4/21 59

• “今年暑假不 AC ?”“是的。”“那你干什么呢?”“看世界杯呀,笨蛋!”“ @#$%^&*%...”

确实如此,世界杯来了,球迷的节日也来了,估计很多 ACMer也会抛开电脑,奔向电视了。作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常 6+7 、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)

23/4/21 60

• Input• 输入数据包含多个测试实例,每个测试实例的第一行

只有一个整数 n(n<=100) ,表示你喜欢看的节目的总数,然后是 n 行数据,每行包括两个数据 Ti_s,Ti_e (1<=i<=n) ,分别表示第 i 个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。 n=0表示输入结束,不做处理。

•  Output• 对于每个测试实例,输出能完整看到的电视节目的个

数,每个测试实例的输出占一行。

23/4/21 61

• 输入:• 1 2 • 1 3 • 3 4 • 0 7 • 3 8 • 15 19• 15 20 • 10 15 • 8 18 • 6 12

5 10 4 14 2 9

0

输出:5

23/4/21 62

先按结束时间排下序(从小到大)

• 1 3 0 3 2 5 6 4 10 8 15 15

• 3 4 7 8 9 10 12 14 15 18 19 20

• 竖着看!• 用心一算:• 1 3 5 10 15

• 3 4 10 15 19

• 本题可以冒泡

• int main()

• {

• int tmp,n,s,e,l[5],w[5];

• n=4;

• for(int i=1;i<=4;i++ )

• {cin>>l[i]; cin>>w[i];}

• for(int i=1;i<n;i++) // 这是对 2 个数组的冒泡,先排第 1 个位置,最小的• {tmp=i;

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

• if (l[j]<l[tmp]) tmp=j;

• s=l[i];l[i]=l[tmp];l[tmp]=s; // 看好• e=w[i];w[i]=w[tmp];w[tmp]=e; //跟着拍了• }

• for(int i=1;i<=4;i++ )

• { cout<<l[i]<<" "<<w[i]<<endl;

• }

• return 0;

• }

63

64

暑假不 AC 的贪婪代码• #include <iostream>

• using namespace std;

• int main()

• {

• int n;

• int inp_s[100],inp_e[100];//开始,结束 2 个数组• int s_tmp,e_tmp;

• while(cin>>n)

• {

• if (n==0) break;

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

• cin>>inp_s[i]>>inp_e[i];65

• //排序• int tmp=0;

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

• {

• tmp=i;

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

• if (inp_e[j]<inp_e[tmp]) tmp=j;

• e_tmp=inp_e[i],inp_e[i]=inp_e[tmp],inp_e[tmp]=e_tmp;

• s_tmp=inp_s[i],inp_s[i]=inp_s[tmp],inp_s[tmp]=s_tmp;

• }

66

输出结果• int sum=0;

• tmp=inp_e[1];

• for(int i=2;i<=n;i++)

• {

• if (tmp<=inp_s[i]) {sum++; tmp=inp_e[i];}

• }

• cout<<sum+1<<endl;

• }

• //cout << "Hello world!" << endl;

• return 0;

• }67

• 课后一定要练习之!

68