NOI Online培训1至26期例题解析(上:1-20期)

写在前面:本文内容整理源自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;
}

课后习题:

39ab5b45bc904c63a0c36bcfe04ad5b3.png

例题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;
}

运算符的优先级如下:(当多个运算符写在同一句代码的时候,计算机会先执行优先级高的运算,然后才会执行优先级相对低的运算。无法确定优先级时,可以用加括号()的方式来保障括号内的部分先计算。)

0247d979880e44598db2f891a8a36237.png

第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+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值