写在前面:本文内容整理源自2020年5月的NOI Online培训,方便广大Oier探讨学习使用。
有不足之处,请大家多多指正,笔者将进行勘误~
使用的编程工具:Dev C++
第1期 主讲人:朱全民 题目:程序的顺序结构 时长:约27分钟
例题:计算长方形的周长和面积
#include <iostream>
using namespace std; //声明命名空间,避免变量重名冲突
int main()
{
int l,w,c,s;
cin>>l>>w; //c+in,cin表示输入
c=2*(l+w);
s=l*w;
cout<<"周长="<<c<<endl; //c+out,cout表示输出,可以连着输出最后用endl换行
cout<<"面积="<<s<<endl;
return 0;
}
练习题:吕布与貂蝉约会,貂蝉迟到了h小时m分钟s秒,请问共迟到了多少秒?
第2期 主讲人:宋新波 题目:顺序结构程序设计范例 时长:约24分钟
例题1:输入圆的半径,计算圆的周长和面积
#include <iostream>
using namespace std;
int main()
{
double r,s,c; //定义3个双精度浮点数
const double pi=3.14; //定义常量双精度浮点数,用来表示圆周率
cin>>r;
s=pi*r*r;
c=2*pi*r;
cout<<"s="<<s<<",c="<<c<<endl;
return 0;
}
例题2:数的交换。输入两个数a,b,程序将a和b的值进行互换。
#include <iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
cout<<"a="<<a<<",b="<<b<<endl;
//请在此处完善程序
//a=2,b=3
a=a+b;//a=5,b=3
b=a-b;//a=5,b=2
a=a-b;//a=3,b=2
cout<<"a="<<a<<",b="<<b<<endl;
return 0;
}
例题3:反向输出。输入1个3位数a,计算输出a的反向值。
#include <iostream>
using namespace std;
int main()
{
//一、分析问题,已知:a,未知:a的反向值
//二、数据定义
int a,b,ge,shi,bai; //定义输入数、输出数,个位、十位、百位
//三、数据输入
cin>>a;
//四、数据计算
ge=a%10;
shi=a/10%10;
bai=a/100;
b=ge*100+shi*10+bai;
//五、数据输出
cout<<b<<endl;
return 0;
}
例题4:鸡兔同笼问题。输入头数和脚数,输出鸡的数量和兔的数量,总有解。
#include <iostream>
using namespace std;
int main()
{
//一、分析问题,已知:头和脚总数,未知:鸡和兔的数量
//二、数据定义
int a,b,x,y; //头数a,脚数b,鸡数x,兔数y
//三、数据输入
cin>>a>>b;
//四、数据计算,先在草稿纸上进行数学计算,列方程式建模求值后写算法,如下
x=(4*a-b)/2;
y=(b-2*a)/2;
//五、数据输出
cout<<"鸡有"<<x<<"只,兔子有"<<y<<"只\n";
return 0;
}
第3期 主讲人:蔺洋 题目:选择性结构程序设计1和2 时长:约19和7分钟
问题引入:已知一元二次方程的三个系数,求一元二次方程的解。
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
double a,b,c,d,x1,x2;
cin>>a>>b>>c;
d=b*b-4*a*c; //计算德尔塔判别式
x1=(-b+sqrt(d))/(2*a); //一元二次方程求根公式
x2=(-b-sqrt(d))/(2*a); //一元二次方程求根公式
cout<<"x1="<<x1<<' '<<"x2="<<x2<<endl;
return 0;
}
上面这个代码没有考虑无实根的情况,所以在输入1,2,3后,会有异常,实际上应该加入对德尔塔是否大于等于0的判定,会用到选择结构(例如if语句)和关系表达式(例如>、<、==)。
例题1:比较两个数的大小
#include<iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
if(a<b)a=b; //把较大的数赋值给a
cout<<a;
return 0;
}
上面用到的一个if选择结构,下面用到两层选择结构(if else):
#include<iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
if(a>b)
cout<<a;
else
cout<<b;
return 0;
}
思考:如何比较两个字符的大小?如何比较布尔变量Ture和False的大小?
例题2:比较三个数中的最大值
#include<iostream>
using namespace std;
int main()
{
int a,b,c,max;
cin>>a>>b>>c;
if(a>b)
max=a;
else
max=b;
if(max<c)
cout<<c;
else
cout<<max;
return 0;
}
下面用逻辑运算符(与&&,或||,非!)来实现:
#include<iostream>
using namespace std;
int main()
{
int a,b,c;
cin>>a>>b>>c;
if((a>b)&&(a>c))cout<<a;
if((b>a)&&(b>c))cout<<b;
if((c>a)&&(c>b))cout<<c;
return 0;
}
例题3:判断2月的天数。根据输入的年份,判定是否闰年,是闰年输出29,不是闰年输出28。
闰年判定条件:能被400整除,或者,能被4整除且不能被100整除。
#include<iostream>
using namespace std;
int main()
{
int y;
cin>>y;
if(y%400==0||y%4==0&&y%100!=0)
cout<<"29";
else
cout<<"28";
return 0;
}
三目运算符,即,表达式1?表达式2:表达式3。当表达式1成立,运行并返回表达式2的结果;当表达式1不成立,运行并返回表达式3的结果。下面用三目运算符进行实现:
#include<iostream>
using namespace std;
int main()
{
int y,day;
cin>>y;
day=(y%400==0||y%4==0&&y%100!=0)?29:28;
cout<<day;
return 0;
}
课后习题:
例题4:输入3个整数,按从大到小的顺序进行输出。
#include<iostream>
using namespace std;
int main()
{
int a,b,c,t;
cin>>a>>b>>c;
if(a<b){t=a;a=b;b=t;} //if语句的语句体要加大括号
if(a<c){t=a;a=c;c=t;}
if(b<c){t=b;b=c;c=t;}
cout<<a<<' '<<b<<' '<<c;
return 0;
}
例题5:判断一个数n能否同时被3和5整除。
#include<iostream>
using namespace std;
int main()
{
int x;
cin>>x;
if(x%3==0&&x%5==0)
cout<<"yes";
else
cout<<"no";
return 0;
}
运算符的优先级如下:(当多个运算符写在同一句代码的时候,计算机会先执行优先级高的运算,然后才会执行优先级相对低的运算。无法确定优先级时,可以用加括号()的方式来保障括号内的部分先计算。)
第4期 主讲人:叶国平 题目:分支结构上、下、答疑 时长:约18+18+13分钟
例题1:编程实现输入百分制成绩,要求输出成绩等级"A"、"B"、"C"、"D"、"E"。90分以上为"A"等,89~80分为"B"等,79~70分为"C"等,69~60分为"D"等,60分以下为"E"等。
以下使用多层if ,else if语句,注意结尾是else。
#include <stdio.h>
using namespace std;
int main()
{
int grade;
scanf("%d",&grade);
if(grade>-90) printf("A\n");
else if(grade>-80) printf("B\n");
else if(grade>=70) printf("C\n");
else if(grade>=60) printf("D\n");
else printf("E\n");
return 0;
}
例题2:输入年份,计算该年份是否为闰年。用多层if else嵌套实现。
注意:以下代码中的if else总是成对出现。
#include <stdio.h>
using namespace std;
int main(){
int y;
scanf ("%d",&y);
if(y%4==0)
if(y%100==0)
if(y%400==0) printf("%d是闰年\n",y); //能被400整除
else printf("%d不是闰年\n",y); //不能被400整除但能被100整除
else printf("%d是闰年\n",y);//能被4整除但能被100整除
else printf("%d不是闰年\n",y); //不能被4整除
}
换一种方式实现,先判定是否被100整除:
#include <stdio.h>
using namespace std;
int main()
{
int y;
scanf("%d",&y);
if(y%100==0)
if(y%400==0)printf("%d是闰年\n",y);//能被400整除
else printf("%d不是闰年\n",y);//不能被400整除但能被100整除
else
if(y%4==0)printf("%d是闰年\n",y);//能被4整除但能被100整除
else printf("%d不是闰年\n",y);//不能被4整除
}
例题3:用Switch语句实现例题1的成绩等级判断。
#include <stdio.h>
using namespace std;
int main()
{
int grade;
scanf("%d",&grade);
switch(grade/10)
{
case 10:
case 9: printf("A\n");break;
case 8: printf("B\n");break;
case 7: printf("C\n");break;
case 6: printf("D\n");break;
default: printf("E\n");
}
}
注意:
1.各个case子句出现的次序对执行结果没任何影响。
2.每一个case能够拥有一条或多条语句,其最大的不同之处在于使用多条语句时不需要用“{}”括起来。
3.多个case可以共用一组执行语句。
4.switch结构中的case子句中的常量表达式的值不允许相同。
5.switch结构允许嵌套。
例题4:输入年份y和月份m,编程输出y年m月有多少天数。
分析:先判定是否2月,如果是,再判定y是否闰年,因为闰年与非闰年的2月不同。除了2月,其他每年的月份天数都一样。
#include <iostream>
using namespace std;
int main()
{
int y,m,d;
cin>>y>>m;
switch(m)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:d=31;break;
case 4:
case 6:
case 9:
case 11:d=30;break;
case 2:if((y%4==0&&y%100!=0)||(y%400==0))
d=29;
else
d=28;
}
cout<<d<<endl;
return 0;
}
答疑:scanf、printf和cin、cout的区别:
答:
1、它们需要包含的头文件不同,scanf和printf需要包含的头文件为stdio.h,而cin和cout要包含iostream的这个头文件。
2、scanf和printf是格式化输入、输出,使用时必须指定输入和输出的数据的类型;
cin和cout是流输入、输出,是c++的类型对象,通过重载了运算符">>"和"<<",使输入输出变得简单,也更安全(不会有数值溢出问题,比如int超过最大值的问题)。cin和cout会根据定义的变量类型,自动通过流转换到定义的格式输出,因此不像scanf和printf每次都要指定格式类型。
3、printf格式化输出也有好处,比如保留小数位数,设置位宽,不足位宽时设置填充字符时比较
方便,而cout比较麻烦,需要额外编写语句。
4、scanf、printf与cin、cout采用不同的缓冲区所以它们的效率也不一样,scanf、printf的效率要
比cin、cout的效率高。所以大规模数据输入输出要用scanf和printf。
第5期 主讲人:屈运华 题目:选择性结构应用(上、下) 时长:约18+18分钟
例题1:简单计算器
一个最简单的计算器,支持+,-.,*,/四种运算。
1.如果出现除数为0的情况,则输出:Divided by zero!
2.如果出现无效的操作符(即不为 +,-,*,/之一),则输出:Invalid operator!
输入只有一行,共有三个参数,其中第1、2个参数为整数,第3个参数为操作符(+,-,*,/)。输出只有一行,一个整数,为运算结果。仅需考虑输入输出为整数的情况,数据输出只有一行,和运算结果不会超过int表示的范围。
样例输入
12+
样例输出3
简单分析:
这个程序需要提示不合法的有两种情况:
1、符号为“/”并且第2个数为0时,提示被0除;
2、符号不是“+”“-”“*”“/”之一时,提示无效操作符,其他情况要分别计算出相应的运算结果。
针对输入符号的判断,考虑使用switch()语句,会比较方便。
#include <iostream>
using namespace std;
int main()
{
int a,b;
char c;
cin>>a>>b>>c;
switch(c)
{
case '+':cout<<a+b<<endl;break;
case '-':cout<<a-b<<endl;break;
case '*':cout<<a*b<<endl;break;
case '/':
if (b==0)
cout<<"Divided by zero!\n";
else
cout<<a/b<<endl;
break;
default :cout<<"Invalid operator!\n";
}
return 0;
}
例题2:数的拆分
输出一个整数的各个位置上的数字,输入一个整数(最大为四位数),输出只有一行这个整数的各个位置上的数字。
样例输入:
4896
样例输出:
4 8 9 6
简单分析:
1、先判定输入的数有几位数(题干说了最大就4位),可以用if语句。
2、根据有几位数,把这个数针对10,100,1000进行取余,求各位上的值最后输出。
#include <iostream>
using namespace std;
int main()
{
int a;
cin>>a;
if (a>0 && a<10) //是一个嵌套的if else if的语句,前一个判断完接着判断下一个
cout<<a;
else if(a>=10 && a<100)
cout<<a/10<<" "<<a%10;
else if(a>=100 && a<1000)
cout<<a/100<<" "<<a/10%10<<" "<<a%10;
else if(a>=1000 && a<10000)
cout<<a/1000<<" "<<a/100%10<<" "<<a/10%10<<" "<<a%10;
return 0;
}
例题3:求方程的解。
求方程ax^2+ bx+c=0的解。(a,b,c为任意数)
当a等于0时,方程为一元一次方程;当a不等于0时,方程为一元二次方程,利用求根公式:
x1=(-b+sqrt(b*b-4*a*c))/(2*a),x2=(-b-sqrt(b*b-4*a*c))/(2*a),求解。
输入一行,包含三个浮点数a,b,c(它们之间以一个空格分开),分别表示方程ax^2+bx+c=0的系数。
输出一行,表示方程的解。
所有实数部分要求精确到小数点后5位,数字、符号之间没有空格。
简单分析:
a等于0时:
b=0,c=0,x为任意解;
b=0,c不为0,x无解;
b不为0,输出x=..
a不等于0时:b^2=4*a*c,则两个实根相等,则输出形式为:x1=x2=....
b^2>4*a*c,则两个实根不等,则输出形式为:x1=...;x2=..,其中x1>x2。
b^2<4*a*c,则无实数解。
#include <cstdio>
#include <cmath>
using namespace std;
int main()
{
double a,b,c,t;
scanf("%lf %lf %lf",&a,&b,&c);
if(a==0)//一元一次方程
{
if(b==0)
if(c==0)
printf("解为任意数");
else
printf("无解");
else
printf("x=%.5lf",-c/b); //5lf表示输出的浮点数小数点后保留5位
}
else //一元二次方程
{
t=b*b-4*a*c;
if(t==0)
printf("x1=x2=%.5lf",-b/(2*a));
else
if(t>0)
printf("x1=%.5lf,x2=%.5lf",-b/(2*a)+sqrt(t)/(2*a),-b/(2*a)-sqrt(t)/(2*a));
else
printf("无实数解");
}
return 0;
}
例题4:万年历
1949年10月1日是星期六,现在输入一个年、月、日,问这一天是星期几?
输入三个整数,年(年份介于1949和3000之间)、月、日
输出是星期几
样例输入:
2020 5 20
样例输出:3
分析题目:
1、题目给出的1949年10月1日星期六,显然是用来当做基准计算星期用的。
2、首先计算出【1949年10月1日距离公元元年1月1日有多少天】记为sum0;计算的时候要先计算闰年,因为闰年天数多1天。
3、接着计算出【输入的日期ymd距离公元元年1月1日有多少天】记为sum;这个计算方式跟上面的一样。
4、把sum-sum0,得到【输入的日期距离1949年10月1日的天数】记为新sum;这样就能用基准来计算输入日期是星期几。
5、把【新sum除以7】记录为x,因为一个星期7天为1个循环,第一天是周六(1949年10月1日周六),第二天周一,以此类推;
6、把得到的x就对应星期几。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int y,m,d,sum,sum0,flag=0;
cin>>y>>m>>d;
sum0=(1949-1)*365+1949/4-1949/100+1949/400;//计算公元元年到1948年12月31日的天数
sum0=sum0+274;//加274表示10月1日是前9个月加1天,这样才是公元元年到1049年10月1日的天数
sum=(y-1)*365+(y-1)/4-(y-1)/100+(y-1)/400;//计算输入的日期到公元元年的天数
if(y%4==0 && y%100!=0||y%400==0) flag=1; //如果是闰年,多1天,用flag=1表示
switch(m)//判断月份是几月份,不同的月份要加对应月份和之前月份的天数,闰年要额外再加1天
{
case 1:sum=sum+d;break;
case 2:sum=sum+31+d;break;
case 3:sum=sum+59+d+flag;break;
case 4:sum=sum+90+d+flag;break;
case 5:sum=sum+120+d+flag;break;
case 6:sum=sum+151+d+flag;break;
case 7:sum=sum+181+d+flag;break;
case 8:sum=sum+212+d+flag;break;
case 9:sum=sum+243+d+flag;break;
case 10:sum=sum+273+d+flag;break;
case 11:sum=sum+304+d+flag;break;
case 12:sum=sum+334+d+flag;break;
}
sum=sum-sum0;//相减得到输入日期距离1949年10月1日的天数
sum=sum%7;//除以星期的循环天数7
switch(sum)
{
case 0:cout<<"星期六";break;
case 1:cout<<"星期日";break;
case 2:cout<<"星期一";break;
case 3:cout<<"星期二";break;
case 4:cout<<"星期三";break;
case 5:cout<<"星期四";break;
case 6:cout<<"星期五";break;
}
return 0;
}
第6期 主讲人:董烨华 题目:程序的循环结构(上、中、下) 时长:约20+16+14分钟
循环语句有三个步骤:
1、循环变量初始化;2、判断循环条件;3、执行循环体,执行循环增量;
根据循环语句格式不同,以上三个步骤的第2和第3步的位置不同。
例题1:给出一个正整数n(n<=10),利用for语句,计算输出1+2+3+…+n的和,以及1*2*3*….*n的乘积(即n!,n的阶乘)
#include<iostream>
using namespace std;
int main()
{
int sum=0,product=1,n;
cin>>n;
for(int i=1;i<=n;++i)
{
sum+=i;
product*=i;
}
cout<<sum<<endl;
cout<<product<<endl;
return 0;
}
例题2:编程输入一个正整数k,输出斐波那契数列第k个数。
算法分析:
1.斐波那契数列指的是这样一个数列:1、1、2、3、5、8、13、21、34、…
这个数列,从第3项开始,每一项都等于前两项之和。
2.存在的循环操作:在当前一轮循环(i>=3),斐波那契数列当前项c等于前两项a、b相
加之和(c=a+b;),b和c又成为下一轮循环的前两项a和b(a=b:b=c;),直到指定k项计算出
(i<=k)
#include<iostream>
using namespace std;
int main()
{
int a=1,b=1,c,k;
cin>>k;
if(k<3) //第1、2两个数直接输出1
cout<<"第"<<k<<"项的值:"<<1<<endl;
else //从第3数开始利用循环计算
{
for(int i=3;i<=k;++i)
{
c=a+b;//c存储第i个数,每个数都等于前面两个数之和
a=b; b=c;
}
cout<<"第"<<k<<"项 的值:"<<c<<endl;
}
return 0;
}
例题3:给出一个正整数n(n<=10),利用while语句,计算输出1+2+…+n的和以及1*2*3*…·*n的乘积(即n!,n的阶乘)
注意:这道题可以用例题1的for语句改写成while语句来实现。
#include<iostream>
using namespace std;
int main()
{
int sum=0,product=1,n;
cin>>n;
int i=1;
while(i<=n) //这里替换成while循环语句
{
sum+=i;
product*=i;
i++;
}
cout<<sum<<endl;
cout<<product<<endl;
return 0;
}
例题4:输入两个正整数m、n,求出它们的最大公约数和最小公倍数。
算法分析:
1.辗转相除法(欧几里德算法)是指两个整数的最大公约数等于其中较小的那个数和两数
相除余数的最大公约数。最大公约数(GreatestCommonDivisor)缩写为GCD。计算公式:
gcd(m,n)= gcd(n,m mod n)。
例如:m=25,n=15,m/n=1余10,n/10=1余5,10/5=2余0,当余数为0,最后一个除法的除
数为5,就是所求最大公约数(25.15)。
2.存在的循环操作:计算当前轮循环m和n相除的余数c(c=m%n),较小数成为下轮循环的m(m=n),余数成为下轮循环的n(n=c)。循环执行条件,余数不为零(m%n!=0),条件不成立输出n,即为最大公约数。
3.最小公倍数=两整数的乘积·两整数最大公约数,m*n/gcb(m,n)。
#include<iostream>
using namespace std;
int main()
{
int m,n,c,d;
cin>>m>>n;
d=m*n;
if(m<n) //这一步用来确保m大于n,这一步可以省略,具体看while的首轮循环
{
int t=m;
m=n;
n=t;
}
while(m%n!=0) //辗转相除法的循环实现
{
c=m%n;
m=n;
n=c;
}
cout<<n<<endl;
cout<<d/n<<endl;
return 0;
}
例题5:(用do-while编写)求最大公约数和最小公倍数。
#include<iostream>
using namespace std;
int main()
{
int m,n,c,d;
cin>>m>>n;
d=m*n;//保存两数乘积求最小公倍数用
if(m<n) //这一步用来确保m大于n
{
int t=m;
m=n;
n=t;
}
do{
c=m%n;m=n;n=c;//循环体,同时实现循环变量增量
}while(c!=0);//循环结束条件
cout<<"最大公约数为:"<<m<<endl; //先执行循环体,m是最小公倍数
cout<<"最小公倍数为:"<<d/m<<endl;
return 0;
}
第7期 主讲人:李曙 题目:多重循环(上、中、下) 时长:约15+15+14分钟
例题1:数字矩阵。
输入数字n,输出n行有规律的数字
样例输入:
5
样例输出:
12345
23451
34512
45123
51234
算法分析:
1、首先要循环输出n行,这是第一重循环,并且还要换行;
2、在每一个循环里,还有2个数列,每个数列也是一次循环;
第一个数列是从1到n;然后依次减少1位
第二个数列是从0到n-1;然后依次增加1位
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
for( int i = 1; i <= n; i++){ //最外层循环
for(int j= i; j <= n; j+