1、第 7 单元 基本数据结构,作者:林厚从,信息学奥赛课课通(C+),第 1 课 结构体的引入和应用,学习目标 1. 理解结构体的概念和应用背景。 2. 学会使用结构体解决一些实际问题。,结构体,在存储和处理大批量数据时,一般会使用数组来实现,但是每一个数据的类型及含义必须一样。如果需要把不同类型、不同含义的数据当作一个整体来处理,如 1000 个学生的姓名、性别、年龄、体重、成绩等,怎么处理呢? C+ 提供了结构体(struct)来解决这类问题。,1. 结构体的定义,C+ 中的结构体是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。 使用结构体,必须要先声明一个结构体类型,再定义
2、和使用结构体变量。结构体类型的声明格式如下: struct 类型名数据类型1 成员名1;数据类型2 成员名2; ;,定义结构体变量,定义结构体变量格式如下: struct 结构体类型名 变量名列表;也可以把结构体类型声明和变量定义合在一起,格式如下: struct 类型名数据类型1 成员名1;数据类型2 成员名2; 变量名;,2. 结构体的使用,结构体变量具有以下特点: (1)可以对结构体变量的整体进行操作。例如:swap(ai,aj) (2)可以对结构体变量的成员进行操作。引用结构体变量中成员的格式为:结构体变量名. 成员名 (3)结构体变量的初始化方法与数组类似。,例1、学生信息,【问题描
3、述】 输入一个学生的信息,包括姓名、性别、年龄、体重,再输出这些信息。 【输入格式】 一行,依次是学生的姓名、性别、年龄、体重。 【输出格式】 一行,依次是姓名、性别、年龄、体重(体重保留一位小数)。 【输入样例】 zhangsan m 20 90.5 【输出样例】 zhangsan m 20 90.5,/p7-1-1 #include using namespace std; struct studentstring name;char sex;int age;double weight; ; int main()student stu;cin stu.name stu.sex stu.ag
4、e stu.weight;cout stu.name “ “ stu.sex “ “ stu.age “ “ ;cout fixed setprecision(1) stu.weight endl;return 0; ,例2、年龄排序,【问题描述】 输入 n 个学生的信息,包括姓名、性别、出生年月。要求按年龄从小到大依次输出这些学生的信息。数据保证没有学生同年同月出生。 【输入格式】 第一行一个整数 n,表示学生人数,n100。 接下来 n 行,每一行依次输入学生的姓名、性别、出生年份、出生月份。 【输出格式】 按年龄从小到大,一行输出一个学生的原始信息。,【输入样例】 5 John male
5、 1999 12 David female 1999 8 Jason male 1998 11 Jack female 1998 8 Kitty female 2000 7 【输出样例】 Kitty female 2000 7 John male 1999 12 David female 1999 8 Jason male 1998 11 Jack female 1998 8,/p7-1-2 #include using namespace std; struct stustring name;string sex;int year,month; ; const int MAXN = 110;
6、 stu aMAXN; int main()int n;cin n;for(int i = 1; i ai.name ai.sex ai.year ai.month;for(int i = 1; i = n; i+)for(int j = i+1; j = n; j+)if(ai.year aj.year | ai.year = aj.year ,例3、猴子选大王,【问题描述】 有 n 只猴子围成一圈,从 1n 编号,大家决定从中选出一个大王。经过协商,决定选大王的规则为:从编号为1的猴子开始报数,报到k的猴子出圈,然后再从下一只开始继续报1到k最后剩下来的那一只就是大王。要求编程从键盘输入
7、n、k,输出成为大王的猴子编号。 【输入格式】 一行两个正整数 n 和 k,2n1000,2k109 。 【输出格式】 一行一个正整数,代表猴王的编号。 【输入样例】 3 2 【输出样例】 3,【问题分析】 本题采用“模拟”法实现。可以定义一个一维数组来模拟猴子在不在圈内,用 ai 代表第 i 只猴子的状态,0 代表出圈,1 代表在圈内。然后不断的扫描这个一维数组,如果猴子在圈内,则计数器加 1;如果不在就不加,当计数器到达 k 时,就把当前这只猴子标志出圈,然后作出一些相关的处理;当还剩一只猴子的时候就停止操作,输出剩下的圈内这只猴子的编号。 以上算法的最大问题是,如果猴子数比较多,不断扫描
8、一维数组的时候会很慢,特别是当大部分猴子都已经出圈,会扫描很多无猴子的“无效”操作。如果建立一个循环链表,那么在扫描的时候就不会出现无猴子还扫描的情况,在猴子出圈的时候只需要在链表中删除一个元素,这样效率会提高很多。,具体操作如下:定义一个结构体 monkey,里面有两个参数,一个代表猴子编号,另一个记录这只猴子的下一只猴子的编号,注意一开始每只猴子的下一只猴子的编号是本身的编号加 1,最后一只猴子的下一只猴子的编号是 1 号。 如果需要删除 3 号猴子,那么只需要把当前 3 号猴子前一只猴子 2 号的下一只猴子序号,指向当前猴子 3号的下一只猴子序号 4。具体程序参考教材228页-229页。
9、,实践巩固,第 2 课 结构体的扩展,学习目标 1. 理解运算符重载的含义及其使用。 2. 理解成员函数的含义及其使用。,结构体的扩展,在 C+ 中,由于类(class)技术的出现使结构体功能得到了很大的增强。本课简单介绍一些与信息学竞赛有关的运算符重载和成员函数知识,其他更详细、复杂的内容请参阅相关书籍和资料。,1. 运算符重载,“运算符重载”常用于解决结构体或自定义数据类型的加法、减法等特殊含义的运算。运算符重载的一般格式为: 类型名 operator 运算符 (const 类型名 变量)const ,例1、作业统计,【问题描述】 为了了解学生的课后作业负担情况,需要统计学生连续若干天完成
10、作业所需的总时间。现在,输入某位学生 n 天完成作业的时间,格式为时、分、秒,最后输出这位学生 n 天完成作业的总时间(秒)。 【输入格式】 第 1 行一个正整数 n,表示有 n 天; 第 2 第 n+1 行,每行 3 个整数,分别代表时、分、秒。 【输出格式】 一行信息,表示这个学生完成作业的总时间,具体格式见输出样例。 【输入样例】 3 1 20 30 1 20 45 1 19 30 【输出样例】 4hour 0minute 45second,【问题分析】 本题需要把若干时间(时、分、秒)相加,但又不是普通的加法运算。所以,可以利用运算符重载,另外定义一个“+”运算,实现对两个结构体变量的
11、加法。,/p7-2-1 #include using namespace std; struct worktime/ 声明一个结构体类型 worktime 记录学生完成作业的时间int hr,minut,sec; / hr,minut,sec 分别代表时分秒worktime operator +(const worktime x)const / 对 + 进行重新定义worktime tmp;tmp.sec = (sec + x.sec) % 60;tmp.minut = (minut + x.minut + (sec + x.sec) / 60) % 60;tmp.hr = hr + x.hr
12、 + (minut + x.minut + (sec + x.sec) / 60) / 60;return tmp; ; int main()worktime stu,sum;int n;cin n;sum.hr = sum.minut = sum.sec = 0;for(int i = 1; i stu.hr stu.minut stu.sec;sum = sum + stu;/ 两个结构体 sum 和 stu 通过重载 + 运算符进行加法运算cout sum.hr ” hour ” sum.minut ” minute ” sum.sec ” second ” ;return 0; ,2.
13、 成员函数,在 C+ 中,允许在结构中定义函数,该函数称为“成员函数”。描述形式如下: struct 结构名 数据成员成员函数 ;,例2、身高问题,【问题描述】 输入 n 个学生的信息,每个学生信息包括姓名、身高、学号。编程输出身高最高的学生的信息。 【输入格式】 第 1 行一个正整数 n,表示学生个数,n100。 以下 n 行,每一行依次输入学生的姓名、身高、学号。 【输出格式】 输出最高的学生信息,如存在身高一样的请输出学号小的那个同学。 【输入样例】 5 John 172 20160302 David 173 20160306 Jason 168 20160309 Jack 152 20
14、160311 Kitty 147 20160319 【输出样例】 David 173 20160306,/p7-2-2 #include using namespace std; struct stustring name;int heigh;int num;void input()cin name heigh num;void output()cout n;for(int i = 1; i maxn.heigh) maxn = ai;else if(ai.heigh = maxn.heigh ,实践巩固,第 3 课 共用体的引入和应用,学习目标 1. 理解共用体的概念和应用背景。 2. 学会
15、使用共用体解决一些实际问题。,共用体,在 C+ 中,共用体也称联合(union),是一种数据格式,能存储不同类型数据,但同一时间只能存储其中的一种类型数据。例如,存放一个人的信息,如果是成年人,存放他的身份证号码;否则,存放他的学籍号。这里面就有两个成员,但是任何一个人只能使用其中的一个。 使用共用体前必须先声明一个共用体类型,才可以定义和使用共用体变量。例如,要声明一个记录个人信息的共用体,可以这样: union person / 声明一个共用体类型 personchar id18; / 声明一个字符数组,存储身份证号码int num; / 声明一个整数,存放学籍号 ;,例1、成绩统计,【问
16、题描述】 兴趣小组收集学员成绩信息,每个学员的成绩有两种表示方法,一种用 best、good、poor 三种等级来表示,还有一种就是直接用分数来表示(百分制)。请保存学员成绩信息,并且统计有多少人是用等级来表示成绩的,用分数来表示成绩的人的平均分是多少(取整就行)。 【输入格式】 第 1 行一个正整数 n,表示学员人数,n1000。 第 2n+1 行,每行一个字符和一个字符串,中间用一个空格隔开。第一个字符表示这个学生成绩类型,有 C、N 两种分别代表等级表示和分数表示,第二个字符串表示成绩信息。,【输出格式】 一行两个整数,分别表示用等级表示成绩的人数和用分数表示成绩人的平均分(取整),中间
17、用一个空格隔开。 【输入样例】 5 C best C good N 90 C poor N 98 【输出样例】 3 94,/p7-3-1 #include using namespace std; int main()union res / 声明一个共用体 reschar rank5; / 用等级表示成绩int x; / 用分数表示成绩;struct stuchar f; / 代表学生用哪种方式记录成绩res score; / 定义 score 为共用体;stu a1001;int n,count = 0, num = 0;cin n;for(int i = 1; i ai.f;switch(
18、ai.f)case C : for(int j = 0; j ai.score.rankj;count+;break;case N : cin ai.score.x;num += ai.score.x;cout count ” ” num/(n-count) endl;return 0; ,实践巩固,第 4 课 文件,学习目标 学会使用 stream 类流文件进行数据的输入输出。,文件,C+ 程序和文件缓冲区交流的方式有流式和 I/O 方式两种。信息学竞赛中一般使用流式文件操作,流式文件类型一般分为 stream 类的流文件和文件指针 FILE 两种,本课只介绍 stream 类的流文件。,1
19、. stream 类流文件的操作,ifstream fin( “ 输入流文件名 ”); 作用是定义一个输入流文件类型变量 fin,初始化指向引号中指定的文本文件。 ofstream fout( “ 输出流文件名 ”); 作用是定义一个输出流文件类型变量 fout,初始化指向引号中指定的文本文件。 fin 变量名; / 或 fout 变量名; 作用是从 fin 文件中输入数据给某个变量,或者把某个变量的值输出到 fout 文件中,这两个语句类似于 cin 和 cout。 fin.close(); / 或 fout.close(); 作用是关闭输入文件、输出文件。如果文件没有关闭,则程序结束时会自
20、动关闭,因此比赛时一般省略不写。,例1、求和,【问题描述】 从文本文件 sum.in 中读入 n 个正整数,要求对这 n 个数中的奇数和偶数分别求和,再将结果写到文本文件 sum.out 中。 【输入格式】 第 1 行一个正整数 n,1n5000; 第 2n+1 行,每行一个正整数,每个数都在 120000 之间。 【输出格式】 共有两行,每行包含一个整数,第一行为输入文件中的所有奇数之和,第二行为输入文件中的所有偶数之和。,【输入样例】 5 3 10 7 5 8 【输出样例】 15 18,/p7-4-1 #include #include using namespace std; ifstr
21、eam fin( “ sum.in ” ); ofstream fout( “ sum.out ” ); int main()int n,x,s1 = 0,s2 = 0;fin n;for(int i = 1; i x;if(x % 2 = 1) s1 += x;else s2 += x;fout s1 endl s2 endl;fin.close();fout.close();return 0; ,2. 文件的重定向,在 C+ 中,cin 使用的输入设备是键盘,称之为“标准输入(stdin)”。cout 使用的输出设备是显示器,称之为“标准输出(stdout)”。C+ 可以使用 freope
22、n 函数把 stdin 和 stdout 重新定向到某一个指定的文件,使原来的标准输入、输出变成指定文件的输入、输出。具体语句格式为: freopen( “ 输入流文件名 ” , ” r ” ,stdin); freopen( “ 输出流文件名 ” , ” w ” ,stdout); 经过重定向后,任何对 stdin、stdout 的操作都变成了对输入流文件、输出流文件的操作。,例2、替换型密码,【问题描述】 简单的替换型密码是很弱的,它通过将每个字母替换成另外一个字母来加密一个字母组成的信息。考虑下面的替换型密码描述: ABCDEFGHIJKLMNOPQRSTUVWXYZ NOPQRSTUV
23、WXYZABCDEFGHIJKLM 这样的描述表示当输入中出现“A”的时候,输出中应该出现的是“N”。同理,每个“B”都变成“O”,以此类推,一直到“Z”都变成“M”。这个特殊的替换型密码的例子被称为“rot13”(旋转 13rotate-13 的简称),有一个有趣的特性:它是自解密的。将信息再加密一次就会得到原始的信息。这样的密码中,单词“CAT”就会成为“PNG”。而句子“NOW IS THE TIMEFOR ALL GOOD PEOPLE TO PROGRAM WELL.”就成了“ABJ VF GUR GVZR SBE NYY TBBQCRBCYR GB CEBTENZ JRYY.”。注
24、意,所有的空格、标点符号以至于任何不在字符集 AZ 中的字符都不变。 请写一个程序来实现替换型密码。,【输入格式】 第 1 行:没有空格隔开的乱序的 26 个字母 AZ,这些字母被用于描述替换型密码。 第2行:一段长度在180之间的内容,这段内容将被加密。不会有小写字母出现。标点符号、空格和数字都可能出现。没有奇怪的字符(像退格、响铃字符之类)出现。 【输出格式】 一行输入内容加密后的一行文本。 【输入样例】 NOPQRSTUVWXYZABCDEFGHIJKLM NOW IS THE TIME FOR ALL GOOD PEOPLE TO PROGRAM WELL. 【输出样例】 ABJ VF
25、 GUR GVZR SBE NYY TBBQ CRBCYR GB CEBTENZ JRYY.,/p7-4-2 #include using namespace std; int main()freopen( “ subcip.in ” , ” r ” ,stdin);freopen( “ subcip.out ” , ” w ” ,stdout);string s,pass;getline(cin,pass);getline(cin,s);for(int i = 0; i s.size(); i+)if(isupper(si) si = passsi - 65;cout s endl;retu
26、rn 0; ,例3、统计奇数,【问题描述】 输入若干正整数,统计并输出其中奇数的个数。 【输入格式】 若干正整数(不超过 10000 个)。 【输出格式】 一行一个数,表示奇数的个数。 【输入样例】 2 4 5 【输入样例】 1,【问题分析】 当不知道输入数据的个数时,可以使用 while(cin x)来不断读入数据,直到文件里所有数据都读完为止。,/p7-4-3 #include using namespace std; int main()int x,s = 0;freopen( “ odd.in ” , ” r ” ,stdin);freopen( “ odd.out ” , ” w ”
27、 ,stdout);while(cin x)if(x % 2 = 1) s+;cout s endl;return 0; ,实践巩固,第 5 课 队列,学习目标 1. 理解队列的概念及其基本操作。 2. 学会使用队列解决一些实际问题。,队列,队列是一种操作(或者说运算)受到限制的特殊线性表。其插入操作限定在表的一端进行,称为“入队”;其删除操作则限定在表的另一端进行,称为“出队”。插入一端称为队尾(rear);删除一端称为队头(front)。,队列,队列也被称作“先进先出”线性表(FIFO,First In First Out)。类似于生活中排队购票,先来先买,后来后买。 在不断入队、出队的过
28、程中,队列将会呈现出以下几种状态: 队空:队列中没有任何元素。 队满:队列空间已全被占用。 溢出:当队列已满,却还有元素要入队,就会出现“上溢(overflow)”;当队列已空,却还要做“出队”操作,就会出现“下溢(underflow)”。两种情况合在一起称为队列的“溢出”。,1. 队列的基本操作(具体参见教材245页-246页),(1) 初始化 (2) 判空 (3) 求队列中实际元素的个数 (4) 入队,入队操作前,需要判断队列是否已满 (5) 出队,出队操作前,需要判断队列是否为空 (6) 取队首元素,2. 循环队列,随着入队与出队操作的不断进行,队头指针在数组中不断向队尾方向移动,而在队
29、头前面产生了一片不能利用的“空闲区”,当队尾指针指向数组最后一个位置,即rear = maxn时,如果再有元素入队就会出现“溢出”,这种溢出称作“假溢出”。如何解决这种情况呢?一种方法是每次出队操作时,都向“空闲区”整体移动一位,带来的后果是时间复杂度高了;另一种方法是让数组首尾相连,形成“环”状,即所谓的“循环队列”。,2. 循环队列,循环队列初始时,front = rear = 0,如果 maxn 个元素一个个依次入队,则 rear = maxn,此时再有元素入队,则它会被存放在 q0 这个单元,也会出现 front = rear = 0,与队空时的状态一样。解决方法是少用一个元素空间,约
30、定数据入队前,测试“队尾指针在循环意义下加 1 后是否等于头指针”作为判断“队满”的条件。循环队列的实际长度为 (rear - front + maxn) % maxn。 循环队列的重要操作修改如下(使用 q0 这个单元): (1)判断队满:如果(rear + 1) % maxn = front,则队列已满。 (2)入队:如果队列未满,则执行:rear = (rear + 1) % maxn;qrear = x; (3)出队:如果队列不为空,则执行:front = (front + 1) % maxn;,3. 队列的应用举例,例1、周末舞会 【问题描述】 假设在周末舞会上,男士们和女士们进入舞
31、厅时,各自排成一队。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。规定每个舞曲只能有一对跳舞者。若两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写一个程序,模拟上述舞伴配对问题。 【输入格式】 第 1 行两个正整数,表示男士人数 m 和女士人数 n,1m,n1000; 第 2 行一个正整数,表示舞曲的数目 k,k1000。,【输出格式】 共 k 行,每行两个数,之间用一个空格隔开,表示配对舞伴的序号,男士在前,女士在后。 【输入样例】 2 4 6 【输出样例】 1 1 2 2 1 3 2 4 1 1 2 2,【问题分析】 显然,舞伴配对的顺序符合“先进先出”,所以用
32、两个队列分别存放男士队伍和女士队伍。然后,模拟 k 次配对:每次取各队队头元素“配对”输出,并进行“出队”和重新“入队”操作。 参考程序参见教材247页-248页。,例2、取牌游戏,【问题描述】 小明正在使用一堆共 K 张纸牌与 N-1 个朋友玩取牌游戏。其中,NK100000,2N100,K 是 N 的倍数。纸牌中包含 M=K/N 张“good”牌和 K-M 张“bad”牌。小明负责发牌,他当然想自己获得所有“good”牌。 他的朋友怀疑他会欺骗,所以他们给出以下一些限制,以防小明耍诈: 1)游戏开始时,将最上面的牌发给小明右手边的人。 2)每发完一张牌,他必须将接下来的 P 张牌(1P10
33、)一张一张地依次移到最后,放在牌堆的底部。 3)以逆时针方向,连续给每位玩家发牌。 小明迫切想赢,请你帮助他算出所有“good”牌放置的位置,以便他得到所有“good”牌。牌从上往下依次标注为 #1,#2,#3,,【输入格式】 第 1 行,3 个用一个空格间隔的正整数 N、K 和 P。 【输出格式】 M 行,从顶部按升序依次输出“good”牌的位置。 【输入样例】 3 9 2 【输出样例】 3 7 8,【问题分析】 方法1、利用普通队列模拟。 结合题目中的条件 1 和 3,可以推出“小明是第 n 个拿到牌的”,根据数据范围大致推出队列存储容量上界为 K + 10 N (100000 / N)
34、= 1100000。 参考程序参见教材248页-249页。方法2、利用循环队列模拟实现。 参考程序参见教材249页-250页。,实践巩固,第 6 课 队列的应用,学习目标 1. 使用队列解决一些实际应用问题。 2. 学会用队列实现简单的宽度优先搜索。,例1、Blah 数集,【问题描述】 数学家高斯小时候偶然间发现一种有趣的自然数集合 Blah。对于以 a 为基的集合 Blah 定义如下: 1)a 是集合 Blah 的基,且 a 是 Blah 的第一个元素; 2)如果 x 在集合 Blah 中,则 2x+1 和 3x+1 也都在集合 Blah 中; 3)没有其他元素在集合 Blah 中了。 现在
35、小高斯想知道如果将集合 Blah 中元素按照升序排列,第 n 个元素会是多少?注意:集合中没有重复的元素。,【输入格式】 一行两个正整数,分别表示集合的基 a 以及所求元素序号 n,1a50,1n1000000。 【输出格式】 一行一个正整数,表示集合 Blah 的第 n 个元素值。 【输入样例1】 1 100 【输出样例1】 418 【输入样例2】 28 5437 【输出样例2】 900585,【问题分析】 根据条件,除了第一个数 a 以外,可以把数集 q 的所有数分成两个子集,一个是用 2x+1来表示的数的集合1,另一个是用 3x+1 来表示的数的集合2,两个集合要保持有序非常容易,只需用
36、两个指针 two 和 three 来记录。其中 two 表示集合1 下一个要产生的数是由 qtwo*2+1 得到,three 表示集合2 下一个要产生的数是由 qthree*3+1 得到。接下来比较 qtwo*2+1 和 qthree*3+1 的大小关系: 1)如果 qtwo*2+1 qthree*3+1,则把 qthree*3+1 加入数集中。 3)如果 qtwo*2+1 = qthree*3+1,则取任意其一加入数集中即可。 所以,本题就是利用队列先进先出的特点,模拟 n 个数依次产生的过程。,/p7-6-1 #include using namespace std; int q10000
37、11; int main()int a,n,x,two,three,rear;cin a n;two = three = rear = 1;q1 = a;while(rear != n)if(2 * qtwo + 1 3 * qthree + 1)rear+;qrear = 3 * qthree + 1;three+; else if(2 * qtwo + 1 3 * qthree + 1)rear+;qrear = 2 * qtwo + 1;two+; elserear+;qrear = 3 * qthree + 1;two+; three+;cout qn endl;return 0; ,
38、例2、关系网络,【问题描述】 有 n 个人,他们的编号为 1n,其中有一些人相互认识,现在 x 想要认识 y,可以通过他所认识的人来认识更多的人(如果 a 认识 b,b 认识 c,那么 a 可以通过 b 来认识 c),求出 x 最少需要通过多少人才能认识 y。 【输入格式】 第 1 行 3 个整数 n、x、y,2n100; 接下来的 n 行是一个 nn 的邻接矩阵, aij=1 表示 i 认识 j,aij=0 表示不认识。 保证 i=j 时,aij=0,并且 aij=aji。 【输出格式】 一行一个整数,表示 x 认识 y 最少需要通过的人数。数据保证 x 一定能认识 y。,【输入样例】 5
39、1 5 0 1 0 0 0 1 0 1 1 0 0 1 0 1 0 0 1 1 0 1 0 0 0 1 0 【输出样例】 2,【问题分析】 本题是求最优值问题。显然,如果 x 和 y 本身就认识,则答案是 0;否则,答案至少为 1。如果 x 和 y 通过 z 这一个人就间接认识,那么答案是 1,可以通过穷举 z 来实现;否则,答案至少为2。如此做下去,一定能找到答案(最大为n-2)。这种算法叫作“宽度优先搜索”,简称“宽搜”,具体实现需要通过一个队列在实现过程中扩展到所有人。 把 x 加入队列并设置为队头元素,设 qx1=0,从队头开始进行宽搜,穷举邻接矩阵的第 x 行,看 x 认识谁(判断
40、axj=1),认识的人(j)全部依次入队,并且 qj1+。如果出现了 y,则输出 qf1-1,结束搜索;否则,取出队头元素继续宽搜。,/p7-6-2 #include using namespace std; int q100102,a110110; bool p110; int main()int i,j,r,f,tmp,x,y,n;memset(p,true,sizeof(p);cin n x y;for(i = 1; i aij;f = r = 1;qf0 = x; qf1 = 0;px = false;while(f = r)tmp = qf0;if(tmp = y)cout qf1-
41、1 endl;return 0;,for(i = 1; i = n; i+)if(aitmp = 1 ,例3、图的宽度优先遍历,【问题描述】 读入一个用邻接矩阵存储的无向图,输出它的宽度优先遍历序列。 【输入格式】 第 1 行 1 个正整数 n,表示图中顶点数,2n100; 接下来的 n 行是一个 nn 的邻接矩阵,aij=1 表示顶点 i 和顶点 j 之间有直接边相连,aij=0 表示没有直接边相连。保证 i=j 时,aij=0,并且 aij=aji。 【输出格式】 输出 1n 的某一种排列,表示从顶点 1 开始,对该图进行宽度优先遍历得到的顶点序列,每两个数之间用一个“-”分隔。,【输入样
42、例】 8 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1 0 【输出样例】 1-2-3-4-5-7-8-6,【问题分析】 图 7.6-1 所示为图的宽度优先遍历。用队列来实现图的宽度优先遍历:先从某个顶点出发,把这个顶点入队,作为队首元素。然后把跟队首元素相连的顶点再依次入队,最后队首元素出队。接着再把新的队首元素相连的所有顶点入队,新的队首元素出队。如此下去,直到所有元素都出队,出队的顺序就
43、是图的宽度优先遍历序列。,/p7-6-3 #include using namespace std; const int N=110; int aNN,qN,hN; int main()int n;cin n;for(int i = 1; i aij;cout 1;q1 = 1; h1 =1;for(int l = 1,r = 1; l = r; l+)int u = ql;for(int i = 1; i = n; i+)if(aui ,实践巩固,第 7 课 栈,学习目标 1. 理解栈的概念及其基本操作。 2. 学会使用栈解决一些实际问题。,栈也是一种操作(或者说运算)受到限制的特殊线性表。
44、其插入和删除操作都限制在表的一端进行,这一端被称为“栈顶(top)”,相对的另一端称为“栈底(bottom)”。插入操作称为“进栈(PUSH)”或者“压栈”,删除操作称为“出栈(POP)”。栈的特点是“先进后出(FILO,First In Last Out)”,1. 栈的基本操作(具体参见教材260页-261页),(1) 初始化 (2) 判空 (3) 求栈中实际元素的个数 (4) 进栈(压栈) (5) 出栈 (6) 取栈顶元素,2. 栈的应用举例,例1、程序员输入问题 【问题描述】 程序员输入程序出现差错时,可以采取以下的补救措施:按错了一个键时,可以补按一个退格符“#”,以表示前一个字符无效
45、;发现当前一行有错,可以按一个退行符“”,以表示“”与前一个换行符之间的字符全部无效。 【输入格式】 输入一行字符,个数不超过 100。 【输出格式】 输出一行字符,表示实际有效字符。 【输入样例】sdfosiffor(ii#=1,#;i.#=8;i+#); 【输出样例】for(i=1;i=8;i+);,【问题分析】 通过栈的操作,模拟这一过程: 1、逐行处理,处理完一行后输出结果、栈重新置空。 2、对于每行,逐个字符处理: 既不是退格符“”,也不是退行符“”,则将该字符压栈; 是退格符“”,则出栈;是退行符“”,就把栈置空。,/p7-7-1 #include using namespace
46、std; int main()freopen( “ editor.in “ , “ r “ ,stdin);freopen( “ editor.out “ , “ w “ ,stdout);char s110;int top = 0;string str;while(getline(cin,str)for(int i = 0; i str.size(); +i)switch(stri)case # :top-; break;case :top=0; break;default:top+; stop = stri;for(int i = 1; i = top; i+) cout si;cout
47、endl;return 0; ,例2、溶液模拟器,【问题描述】 小谢虽然有很多溶液,但是还是没有办法配成想要的溶液,因为万一倒错了就没有办法挽回了。因此,小谢到网上下载了一个溶液配置模拟器。模拟器在计算机中构造一种虚拟溶液,然后可以虚拟地向当前虚拟溶液中加入一定浓度、一定体积的这种溶液,模拟器会快速地算出倒入后虚拟溶液的浓度和体积。当然,如果倒错了可以撤销。 模拟器的使用步骤如下: 1)为模拟器设置一个初始体积和浓度 V0、C0%。 2)进行一系列操作,模拟器支持两种操作: P(v,c)操作:表示向当前的虚拟溶液中加入体积为 v 浓度为 c 的溶液; Z 操作:撤销上一步的 P 操作。,【输入
48、格式】 第一行两个整数,表示 V0 和 C0,0C0100; 第二行一个整数 n,表示操作数,n10000; 接下来 n 行,每行一条操作,格式为:P_v_c 或 Z。 其中 _ 代表一个空格,当只剩初始溶液的时候,再撤销就没有用了。 任意时刻质量不会超过 231 -1。 【输出格式】 n 行,每行两个数 Vi,Ci,其中 Vi 为整数,Ci 为实数(保留 5 位小数)。 其中,第 i 行表示第 i 次操作以后的溶液体积和浓度。 【输入样例】 100 100 2 P 100 0 Z 【输出样例】 200 50.00000 100 100.00000,【问题分析】 使用栈来模拟实现: 1) 读入
49、撤销时,栈顶元素出栈。 2) 读入溶液时,把新的溶液的体积和浓度压栈。 3) 每次操作完,输出栈顶的体积和浓度。,/p7-7-2 #include using namespace std; const int N = 10010; struct nodeint w;double c; aN; int main()freopen( “ simulator.in ” , ” r ” ,stdin);freopen( “ simulator.out ” , ” w ” ,stdout);int v,n,c,top = 1;cin v c n;atop.w = v,atop.c = c; / 将初始溶液入栈while(n-)char ch;cin ch;if(ch = Z ,