Processing math: 1%
NOIP学习小站
西安交通大学附属中学航天学校

顺序结构例题

本小节通过顺序结构例题解析,更加深入探讨顺序结构。例题问题的解决通过编程解决问题的思路和步骤来组织,希望大家要注意并养成好的编程习惯,注重编码前问题的分析和算法的设计!

1.海伦公式计算三角形面积

问题背景:已知三角形的三边长 a、b、c,则三角形的面积可使用海伦公式来计算:

p = (a+b+c)/2,则三角形面积 S = \sqrt{p(p-a)(p-b)(p-c)}

问题描述:计算给定三边长的三角形的面积

输入格式:三角形的三边长,用空格隔开(数据保证能组成三角形)

输出格式:三角形的面积,保留2位小数

问题分析:输入三个浮点数存储到double变量中,利用问题背景提供的公式来计算

算法设计

  1. Input \ a,b,c
  2. p ← (a+b+c)/2
  3. s ← sqrt(p*(p-a)*(p-b)*(p-c))
  4. Output \ s
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<cstdio>
#include<cmath>
int main()
{
double a,b,c,p,s;
scanf("%lf%lf%lf",&a,&b,&c);
p = (a+b+c)/2;
//表达式中*不能省略
s = sqrt(p*(p-a)*(p-b)*(p-c));
printf("%.2lf",s);
return 0;
}
#include<cstdio> #include<cmath> int main() { double a,b,c,p,s; scanf("%lf%lf%lf",&a,&b,&c); p = (a+b+c)/2; //表达式中*不能省略 s = sqrt(p*(p-a)*(p-b)*(p-c)); printf("%.2lf",s); return 0; }
#include<cstdio>
#include<cmath>
int main()
{
	double a,b,c,p,s;
	scanf("%lf%lf%lf",&a,&b,&c);
	p = (a+b+c)/2;
	//表达式中*不能省略
	s = sqrt(p*(p-a)*(p-b)*(p-c));
	printf("%.2lf",s);
	return 0;
}

注意,实际编程时需要考虑代码的可读性,例如下面的代码虽然更加精简,但是阅读起来会有点费解:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<cstdio>
#include<cmath>
int main()
{
//不建议编写如下“看似精简,但是可读性不高的代码”
double a,b,c;
scanf("%lf%lf%lf",&a,&b,&c);
printf("%.2lf",sqrt((a+b+c)/2*((a+b+c)/2-a)*((a+b+c)/2-b)*((a+b+c)/2-c)));
return 0;
}
#include<cstdio> #include<cmath> int main() { //不建议编写如下“看似精简,但是可读性不高的代码” double a,b,c; scanf("%lf%lf%lf",&a,&b,&c); printf("%.2lf",sqrt((a+b+c)/2*((a+b+c)/2-a)*((a+b+c)/2-b)*((a+b+c)/2-c))); return 0; }
#include<cstdio>
#include<cmath>
int main()
{
	//不建议编写如下“看似精简,但是可读性不高的代码”
	double a,b,c;
	scanf("%lf%lf%lf",&a,&b,&c);
	printf("%.2lf",sqrt((a+b+c)/2*((a+b+c)/2-a)*((a+b+c)/2-b)*((a+b+c)/2-c)));
    return 0;
}

2.数的拆分——求逆序数

问题描述:求一个三位正整数的逆序数,所谓逆序说就是将整数逆序书写得到的整数,例如123的逆序数是321;120的逆序数是21(去掉高位多余的0)

输入格式:一个三位正整数n

输出格式:输出n的逆序数,逆序数高位不能有多余的0

问题分析:将三位正整数 n 每位上的数字“拆分”出来,个位、十位、百位上的数字分别存储到变量 g、s、b 中,然后依次输出g、s、b,不过不用任何符号隔开,那么看上去就是一个三位数,也就是要求的逆序数。那么问题只剩下如何“拆分”了:来看两个运算 \%/n\%10的结果就是个位上的数字g;要计算十位上的数字b,先来看n/10(整数/整数结果是整数!)的结果是把n的个位数“砍掉”了,原来十位上的数到了结果的个位数上,那么结果%10,也就是n\ / 10\ \%\ 10就是十位上的数字s;同样的n\ / \ 100\ \%\ 10(对于三位数来说n/100也行)就是百位上的数字b

算法设计:

  1. Input \ n
  2. g \ ← \ n \% 10
  3. s \ ← \ n/10 \% 10
  4. b \ ← \ n/100 \% 10
  5. Output \ g,s,b(不用任何符号隔开)
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
using namespace std;
int main()
{
int n,g,s,b;
cin>>n;
g = n%10;
s = n/10%10;
b = n/100%10;
cout<<g<<s<<b;
return 0;
}
#include<iostream> using namespace std; int main() { int n,g,s,b; cin>>n; g = n%10; s = n/10%10; b = n/100%10; cout<<g<<s<<b; return 0; }
#include<iostream>
using namespace std;
int main()
{
    int n,g,s,b;
    cin>>n;
    g = n%10;
    s = n/10%10;
    b = n/100%10;
    cout<<g<<s<<b;
    return 0;
} 

程序运行测试情况如下:

输入123,输出:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
321
321
321

输入120,输出:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
021
021
021

输入100,输出:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
001
001
001

分析测试结果,可知右边两种情况(个位是0或者除百位外全是0),直接输出拆分出来的个、十、百位数的数字,不能满足题目“逆序数高位不能为0”的要求。那么我们设计的算法还不能全面地解决问题,需要进行调整。其实拆出来g、s、b后,可以再重新组合成新整数:g*100+s*10+b,这也是 n 的逆序数,并且这样做可以自动去掉高位多余的0。我们可以自行修改代码,并再次进行全面的测试。

其实这个问题还可以利用scanf("%1d%1d%1d",&b,&s,&g);的方式输入数据,或者使用字符方式输入数据,这样就不用拆分整数了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<cstdio>
int main()
{
int n,g,s,b;
scanf("%1d%1d%1d",&b,&s,&g);
n = g*100+s*10+b;
printf("%d",n);
return 0;
}
#include<cstdio> int main() { int n,g,s,b; scanf("%1d%1d%1d",&b,&s,&g); n = g*100+s*10+b; printf("%d",n); return 0; }
#include<cstdio>
int main()
{
    int n,g,s,b;
    scanf("%1d%1d%1d",&b,&s,&g);
    n = g*100+s*10+b;
    printf("%d",n);
    return 0;
} 
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<cstdio>
int main()
{
int n;
char g,s,b;
scanf("%c%c%c",&b,&s,&g);
//b,s,g都是数字字符,减去'0'就是对应的整数
n = (g-'0')*100+(s-'0')*10+(b-'0');
printf("%d",n);
return 0;
}
#include<cstdio> int main() { int n; char g,s,b; scanf("%c%c%c",&b,&s,&g); //b,s,g都是数字字符,减去'0'就是对应的整数 n = (g-'0')*100+(s-'0')*10+(b-'0'); printf("%d",n); return 0; }
#include<cstdio>
int main()
{
    int n;
	char g,s,b;
    scanf("%c%c%c",&b,&s,&g);
    //b,s,g都是数字字符,减去'0'就是对应的整数 
    n = (g-'0')*100+(s-'0')*10+(b-'0');
    printf("%d",n);
    return 0;
}

3.[洛谷]P1425 小鱼的游泳时间

初步分析:游泳小时数为c-a,分钟数为d-b

算法设计:

  1. Input \ a,b,c,d
  2. Output \ c-a
  3. Output \ d-b
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
using namespace std;
int main()
{
int a,b,c,d;
cin>>a>>b>>c>>d;
cout<<c-a<<" "<<d-b;
return 0;
}
#include<iostream> using namespace std; int main() { int a,b,c,d; cin>>a>>b>>c>>d; cout<<c-a<<" "<<d-b; return 0; }
#include<iostream>
using namespace std;
int main()
{
	int a,b,c,d;
	cin>>a>>b>>c>>d;
	cout<<c-a<<" "<<d-b;
	return 0;
}

测试结果如下:

输入数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
8 10 12 20
8 10 12 20
8 10 12 20

输出数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
4 10
4 10
4 10

输入数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
12 50 19 10
12 50 19 10
12 50 19 10

输出数据:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
7 -40
7 -40
7 -40

从测试结果可知,算法有问题!原因是没有考虑d<b的情况,此时是不能直接用d-b作为分钟数(不用担心a,c的大小情况,合法的输入数据肯定满足a \le c)。看来需要重新设计算法。计算00:00到开始时间a时b分的总分钟数:t1 = a*60+b,00:00到结束时间c时d分的总分钟数:t2 = c*60+d,那么游泳持续时间(分钟数)t = t2-t1,换算成小时数为t/60,分钟数为t\%60

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
using namespace std;
int main()
{
int a,b,c,d,t;
cin>>a>>b>>c>>d;
t = (c*60+d)-(a*60+b);
cout<<t/60<<" "<<t%60;
return 0;
}
#include<iostream> using namespace std; int main() { int a,b,c,d,t; cin>>a>>b>>c>>d; t = (c*60+d)-(a*60+b); cout<<t/60<<" "<<t%60; return 0; }
#include<iostream>
using namespace std;
int main()
{
	int a,b,c,d,t;
	cin>>a>>b>>c>>d;
	t = (c*60+d)-(a*60+b);
	cout<<t/60<<" "<<t%60;
	return 0;
}

4.[洛谷]P1421 小玉买文具

问题分析:和上一题类似,这里有元、角2个单位,可以先统一单位,例如全部转换成角,那直接做除法就行(整数/整数结果是整数!)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
using namespace std;
int main()
{
int a,b;
cin>>a>>b;
cout<<(a*10+b)/19;
return 0;
}
#include<iostream> using namespace std; int main() { int a,b; cin>>a>>b; cout<<(a*10+b)/19; return 0; }
#include<iostream>
using namespace std;
int main()
{
	int a,b;
	cin>>a>>b;
	cout<<(a*10+b)/19;
	return 0;
} 

转换成角来计算,计算简单。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
using namespace std;
int main()
{
int a,b,ans;
cin>>a>>b;
ans = (a+0.1*b)/1.9;
cout<<ans;
return 0;
}
#include<iostream> using namespace std; int main() { int a,b,ans; cin>>a>>b; ans = (a+0.1*b)/1.9; cout<<ans; return 0; }
#include<iostream>
using namespace std;
int main()
{
	int a,b,ans;
	cin>>a>>b;
	ans = (a+0.1*b)/1.9;
	cout<<ans;
	return 0;
} 

转换成元来计算,计算复杂(除法结果为浮点数,需要转换成整数)。本例因为小数位数仅有一位,浮点数除法计算在可靠范围。但是有些问题用浮点数计算就可能不可靠了。记住:能够避免浮点运算尽量避免!

5.[洛谷]P5709 Apples Prologue

问题分析:总时间为s分钟,吃一个苹果花费t分钟,那么吃掉的苹果数应该是s/t,要计算剩下的完整苹果数量,而s/t结果只是除法的整数部分,已经吃过的苹果(包括没有吃完的)的数量是(int)ceil(1.0*s/t)ceil向上取整,结果是浮点数,还需要int强制类型转换),剩余苹果数量是m-(int)ceil(1.0*s/t)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int m,t,s;
cin>>m>>t>>s;
cout<<m-(int)ceil(1.0*s/t);
return 0;
}
#include<iostream> #include<cmath> using namespace std; int main() { int m,t,s; cin>>m>>t>>s; cout<<m-(int)ceil(1.0*s/t); return 0; }
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	int m,t,s;
	cin>>m>>t>>s;
	cout<<m-(int)ceil(1.0*s/t);
	return 0;
} 

在洛谷平台提交后,评测结果如下:

三个点错误。鼠标放到错误点上,显示了错误原因:Wrong Answer.wrong answer On line 1 column 1.read -,except 0.


测评的过程是这样的:每个测试点都设计了标准的输入数据和对应的标准输出答案。测评时会将提交的程序编译后去“跑”每个测试点的输入数据,程序的结果和测试点的标准结果对比,如果相同则认为该点通过,否则未通过。

这里给出的错误原因指出读到程序输出的第1行(line 1)第1列(column 1)内容是 -,但是该处期望(expected,也就是标准答案)的内容是0。

注意错误提示不会提示大片区域错误,只会提示在某行某列处第一次匹配不上(内容不同),例如程序运行结果是123,但是标准答案是136,那么只会提醒第一处不匹配的内容,也就是 line 1 column 2,此时错误信息可能是:Wrong Answer.wrong answer On line 1 column 2.read 2,except 3.

此外,测评系统匹配程序输出结果和标准答案时,对于每一行末尾的空格,以及整个内容后的空行,一般都会忽略不计(包括洛谷平台、竞赛时的测评)。例如题目要求输出1行,我们使用cout<<结果<<endl;cout<<结果;都没有问题。


结合题目和错误提示信息,可以推测应该是m-(int)ceil(1.0*s/t)的计算结果出现了负数(提示信息给出“在第一行第一列读到-”的原因),这个时候按照题意,应该输出0。结果应该取m-(int)ceil(1.0*s/t)和0的最大值,可以借助max函数实现(后续我们还可以通过选择结构的if语句实现):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int m,t,s;
cin>>m>>t>>s;
cout<<max(0,m-(int)ceil(1.0*s/t));
return 0;
}
#include<iostream> #include<cmath> using namespace std; int main() { int m,t,s; cin>>m>>t>>s; cout<<max(0,m-(int)ceil(1.0*s/t)); return 0; }
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	int m,t,s;
	cin>>m>>t>>s;
	cout<<max(0,m-(int)ceil(1.0*s/t));
	return 0;
} 

登录

注册