本题是浙江理工大学ACM入队200题第四套中的F题

我们先来看一下这题的题面.


由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例.

  • 样例相当于给你举了个具体的例子,可以帮助你更好的理解题目
  • 样例会告诉你输入和输出的格式,你必须要在程序里以这样的格式输入和输出,否则会出问题
  • 样例可以在你本地写完代码之后用作测试,来检查你的代码能否正常地运行(不过样例运行正确并不代表完全对了,可能输入其他的数据会出现别的问题)

题面

题目描述

求ax2+bx+c=0方程的实根。a,b,c由键盘输入. 解方程要考虑系数a等于零的情况,且解x1、x2必须是float型。a等于零有两种情况(b == 0,b!=0),a不等于零有三种情况(delta>0、==0、<0),先计算得到x1、x2,再printf输出

输入

输入三个数a,b,c

输出

输出方程的实根,如果方程有实根,则输出根;如果方程有2个不等实根,则分2行输出,第一行输出较大根,第二行输出较小根。 其余情况(如无实根等)则输出No

样例输入

1 -3 2

样例输出

2.000000

1.000000


代码风格问题

这题在ACM成员群里询问的频率的非常高,但其实并不是一道hard题,只是判断条件比较多容易乱而已,并不涉及什么高级的数据结构和算法.

在讲思路之前,首先来说说问这道题的朋友大多出现的两个坏习惯,不缩进和代码块不喜欢加大括号.

如果你想直接看与这题有关的解释,可以跳过这部分.

缩进问题

首先,一定要养成好好缩进的习惯.不缩进的代码在C中并不会报错,但是会极大降低自己代码的可读性.

可读性是很重要的,不管是实际开发还是ACM竞赛,都不是一个人的战场,你的代码是需要给你的同伴和队员看的,同时也是需要给未来的自己看的.你也不希望别人看不懂你的代码而导致时间成本的增加吧?

缩进的本质目的是为了展现出代码的层次,一份良好缩进的代码可以很清楚地展示出代码的层次结构,帮助阅读者更好地理解代码.

如果你很想养成好好缩进的习惯但是苦于经常忘记缩进或者不知道具体应该怎么缩进的话,强烈建议有空去学习一下Python语言,在Python中,不缩进会抛出异常,你必须要进行缩进.同时Python在ACM涉及到高精度的算法中,也有出场的一席之地.

大括号问题

其次就是不写大括号的问题了,这边我强烈建议所有新ACMer朋友们不要去管什么可以不写大括号的规则,一律打上大括号!在你不完全熟悉什么地方可以省略大括号的情况下随意省略大括号都可能会导致极其严重的问题!打一下大括号也不是什么特别麻烦的事情,所以请一律打上大括号!

我们来看看一些真实的不打大括号导致出问题的案例,均来自ACM成员群,进行过简化和改编.

错误样例1 不打大括号的嵌套if-else语句

首先,下面如下不打大括号的嵌套if-else语句:

	int a = 10;
if(a > 5)
if(a > 10)
printf("a > 10");
else
printf("a < 5,I think");

从缩进上看,写出这样代码的人似乎想实现a > 10时输出"a > 10"a <= 5时输出"a < 5,I think".但是,实际上根据else的匹配规则,此处的else是属于if(a > 10)的,所以此处的代码在运行之后会很滑稽地输出"a < 5,I think".但是打上大括号就可以直接解决并永远规避这个问题:

	int a = 10;
if(a > 5)
{
if(a > 10)
{
printf("a > 10");
}
}
else
{
printf("a < 5,I think");
}

错误样例2 不打大括号的冒泡排序

接着,我们来看下面这一段不打大括号的冒泡排序代码(你不知道冒泡排序也没关系,只要看不打大括号的问题就好了,感兴趣可以去看我写的这篇博客,有详细的推出过程):

	for (int i = 0; i < len; i++)
for(int j = len - 1;j > i;j--)
if(a[j - 1] < a[j])
int t = a[j];a[j] = a[j - 1];a[j - 1] = t;

根据冒泡排序的实现方式以及这位同学代码的写法,可以明白他是希望在a[j - 1] < a[j]满足时执行数据交换需要进行的这三句代码.尽管他很呆地把这三句代码写在了一行上,但依旧改变不了if语句在无大括号的时候只管一行语句的现实,要实现正常的功能,这里if语句应当加上大括号.

	for (int i = 0; i < len; i++)
{
for(int j = len - 1;j > i;j--)
{
if(a[j - 1] < a[j])
{
int t = a[j];a[j] = a[j - 1];a[j - 1] = t;
}
}
}

限于篇幅原因这里就不再列举其他的自以为是不写大括号导致错误的错误样例了.总之,在没有完全熟悉if语句大括号省略规则的情况下,请完整地打出所有的大括号.另外即便你已经非常熟悉大括号省略的规则,也强烈建议不要省略任何大括号.仅仅打一对大括号并不是什么难事.

题目分析

我们完全可以模拟我们自己用纸笔解形二次方程的过程,慢慢写出各个分支语句.

首先,相信经历过高中毒打的各位朋友们在解一个形二次方程,第一反应都是讨论二次项系数是否为零.当二次项系数为零时,这便是一个形一次方程,同样要再讨论一下一次项系数是否为零,由数学知识可以得出这部分的代码:

	// 定义3个double型变量a,b,c,注意这里使用直接定义为double型来解决整数除以整数还是整数的问题,下同.这题其实存在一个潜在的问题,即double中存在负0,这里我们直接无视这种情况(实际上此题特判-0反而会导致答案错误).
if (a == 0)
{
// 当前是形一次方程
if (b == 0)
{
// 一次项系数也为0,无穷解或无解
printf("No\n");
}
else
{
// 一次项系数不为0,为一次方程,直接解就好了(不知道为啥这里有朋友没有负号,或者是写成b / c,这边建议口算不行还是打个草稿好)
printf("%lf\n", -c / b);
}
}

如果掉进了此处对两个整数使用除法并企图得到一个小数的结果的老坑,或者对此还不熟悉的朋友可以去看看我写的这篇博客

之后就是解二次方程的问题了,为了后续编写方便,我们先计算delta:

	double delta = b * b - 4 * a * c;

当delta小于零时,方程无解,我们直接输出No:

	if(delta < 0)
{
// 方程无解
printf("No\n");
}

接下来,欢迎来到无数朋友的掉坑点.


听取WA声一片

常见错误思路及解决方案

优先级问题

当delta等于0时,二次方程仅有一解,相信经历过初中和高中朋友们都能很熟练地写出求这个唯一解的公式吧,于是就有朋友给出了如下代码(局部且无视if-else结构):

	if(delta == 0)
{
// 方程仅有一解
printf("%lf\n", -b / 2 * a);
}

这里掉进的便是运算符优先级的坑.在数学中,同级运算符是从左往右运算的,在C中大体也是这样(不考虑结合性).因而在C语言中和数学一样,我们把一个分式改为除法运算式时,需要对分子分母添加括号(如果只有一项那当然不用加),这样才能保证优先级不出问题.局部参考代码如下(无视if-else结构):

	if(delta == 0)
{
// 方程仅有一解
printf("%lf\n", -b / (2 * a)); // 时刻关注运算符优先级
}

想当然未实现由大到小输出

接下来当delta大于0时,依据题目要求我们需要将两根从大到小输出,于是就有很多朋友们给出如下代码(写成这样来问的也是最多的):

	if(delta > 0)
{
// 方程有两解
double x1 = (-b + sqrt(delta)) / (2 * a); // 记得导入<math.h>头文件鸭!
double x2 = (-b - sqrt(delta)) / (2 * a);
printf("%lf\n%lf\n", x1, x2);
}

这里问题出在哪了呢?出在了没有将两根从大到小输出.可是每次当我指出这个问题时,都会有朋友们反驳道:"我考虑了啊,我就是从大到小输出的啊!"之所以出现这种情况,是因为这些朋友们想当然地以为二次方程求根公式中+的那个大而-的那个小.

实际上很明显并不是这样的,比如我们使用-3 4 5输入时,得到的输出便是-0.786300 2.119633(见下图)



这明显不是从大到小输出,因为决定这两个数大小的不仅是+还是-根号delta,还有其他的因素.当然,其他的因素同样不仅限于a的正负,有些朋友很呆地通过判断a的正负来当做判断两根的大小,这也是想当然了.

那怎么办呢,很容易啊,这不就是两个数降序输出的问题吗?直接比较交换就可以了(当然也有其他处理方法),局部参考代码如下(无视if-else结构):

	if (delta > 0)
{
// 方程有两解
double x1 = (-b + sqrt(delta)) / (2 * a); // 记得导入<math.h>头文件鸭!
double x2 = (-b - sqrt(delta)) / (2 * a);
// 如果不满足降序,交换x1和x2
if (x1 < x2)
{
double t = x1;
x1 = x2;
x2 = t;
}
printf("%lf\n%lf\n", x1, x2);
}

参考代码

下面给出了我自己做这道题时候的完整代码:

(仅作为参考,一定要自己写一下奥,作弊没意思,害人又害己)

#include <stdio.h>
#include <math.h> int main()
{
double a, b, c; // 这里使用直接定义为double型来解决整数除以整数还是整数的问题.这题其实存在一个潜在的问题,即double中存在负0,这里我们直接无视这种情况(实际上此题特判-0反而会导致答案错误).
scanf("%lf%lf%lf", &a, &b, &c);
if (a == 0)
{
// 当前是形一次方程
if (b == 0)
{
// 一次项系数也为0,无穷解或无解
printf("No\n");
}
else
{
// 一次项系数不为0,为一次方程,直接解就好了(不知道为啥这里有朋友没有负号,或者是写成b / c,这边建议口算不行还是打个草稿好)
printf("%lf\n", -c / b);
}
}
else
{
double delta = b * b - 4 * a * c;
if (delta < 0)
{
// 方程无解
printf("No\n");
}
else if(delta == 0)
{
// 方程仅有一解
printf("%lf\n", -b / (2 * a)); // 时刻关注运算符优先级
}
else
{
// 方程有两解
double x1 = (-b + sqrt(delta)) / (2 * a); // 记得导入<math.h>头文件鸭!
double x2 = (-b - sqrt(delta)) / (2 * a);
// 如果不满足降序,交换x1和x2
if (x1 < x2)
{
double t = x1;
x1 = x2;
x2 = t;
}
printf("%lf\n%lf\n", x1, x2);
}
} return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

这篇题解就到这里了,各位朋友如果有问题欢迎到acm成员群中提问哦!

「浙江理工大学ACM入队200题系列」问题 F: 零基础学C/C++39——求方程的解的更多相关文章

  1. 「浙江理工大学ACM入队200题系列」问题 E: 零基础学C/C++78——求奇数的乘积

    本题是浙江理工大学ACM入队200题第八套中的E题 我们先来看一下这题的题面. 题面 输入 输入数据包含多个测试实例,每个测试实例占一行,每行的第一个数为n,表示本组数据一共有n个,接着是n个整数,你 ...

  2. 「浙江理工大学ACM入队200题系列」问题 B: 零基础学C/C++12——求平均值

    本题是浙江理工大学ACM入队200题第二套中的B题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...

  3. 「浙江理工大学ACM入队200题系列」问题 L: 零基础学C/C++85——完美数

    本题是浙江理工大学ACM入队200题第八套中的L题 我们先来看一下这题的题面. 题面 题目描述 任何一个自然数的约数中都有1和它本身,我们把小于它本身的因数叫做这个自然数的真约数. 如6的所有真约数是 ...

  4. 「浙江理工大学ACM入队200题系列」问题 K: 零基础学C/C++84——奇偶ASCII值判断

    本题是浙江理工大学ACM入队200题第八套中的K题 我们先来看一下这题的题面. 题面 题目描述 任意输入一个字符,判断其ASCII是否是奇数,若是,输出YES,否则,输出NO; 例如,字符A的ASCI ...

  5. 「浙江理工大学ACM入队200题系列」问题 J: 零基础学C/C++83——宁宁的奥数路

    本题是浙江理工大学ACM入队200题第八套中的J题 我们先来看一下这题的题面. 题面 题目描述 宁宁参加奥数班,他遇到的第一个问题是这样的:口口口+口口口=口口口,宁宁需要将1~9 九个数分别填进对应 ...

  6. 「浙江理工大学ACM入队200题系列」问题 L: 零基础学C/C++52——计算数列和2/1,3/2,5/3,8/5......

    本题是浙江理工大学ACM入队200题第五套中的L题 我们先来看一下这题的题面. 题面 题目描述 有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13,-- 计算这个数列的前n项和.注意: ...

  7. 「浙江理工大学ACM入队200题系列」问题 A: 零基础学C/C++34—— 3个数比较大小(冒泡排序与选择排序算法)

    本题是浙江理工大学ACM入队200题第四套中的A题,同时给出了冒泡排序和选择排序算法 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习 ...

  8. 「浙江理工大学ACM入队200题系列」问题 H: 零基础学C/C++18——三位数反转

    本题是浙江理工大学ACM入队200题第二套中的H题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...

  9. [Python] 文科生零基础学编程系列二——数据类型、变量、常量的基础概念

    上一篇:[Python] 文科生零基础学编程系列--对象.集合.属性.方法的基本定义 下一篇: (仍先以最简单的Excel的VBA为例,语法与Python不同,但概念和逻辑需要理解透彻) p.p1 { ...

随机推荐

  1. 【NOI P模拟赛】寻找道路(bfs,最短路)

    题面 一道特殊的最短路题. 给一个 n n n 个点 m m m 条有向边的图,每条边上有数字 0 \tt0 0 或 1 \tt1 1 ,定义一个路径的长度为这个路径上依次经过的边上的数字拼在一起后在 ...

  2. 使用VS Code 搭建 platformio 平台

    一.需要的资源网站 arduino GitHub:https://github.com/arduino espressif GitHub:https://github.com/espressif pl ...

  3. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  4. 【长文详解】TypeScript、Babel、webpack以及IDE对TS的类型检查

    只要接触过ts的前端同学都能回答出ts是js超集,它具备静态类型分析,能够根据类型在静态代码的解析过程中对ts代码进行类型检查,从而在保证类型的一致性.那,现在让你对你的webpack项目(其实任意类 ...

  5. JAVA反序列化漏洞修复解决方法

    MyObject类建立了Serializable模块,而且重新写过了readObject()变量,仅有建立了Serializable模块的类的目标才能够被实例化,沒有建立此模块的类将无法使他们的任意状 ...

  6. KingbaseES 缺少库文件问题

    在工作中大家经常会遇到找不到某个so 的问题,这类可能是so文件缺失,或者是由于LD_LIBRARY_PATH 环境变量设置不当的原因. 1.库文件 我们通常把一些公用函数制作成函数库,供其它程序使用 ...

  7. 当 SQL DELETE 邂逅 Table aliases,会擦出怎样的火花

    开心一刻 晚上,女儿眼噙泪水躺在床上 女儿:你口口声声说爱我,说陪我,却天天想着骗我零花钱,你是我亲爹吗? 我:你想知道真相 女儿:想! 我:那你先给爸爸两百块钱! 环境准备 MySQL 不同版本 利 ...

  8. G&GH01 注册/安装/设置

    注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...

  9. KeeWiDB:兼容Redis协议,领跑NoSQL

    如果现在的我们离开了互联网,生活会是什么样子? 互联网++++,已经深刻渗透到人们的生活中. 不知道大家有没有想过?每一个互联网+结合的背后都是海量的存储需求.你查看的每一个商品.组建的每一个战队.阅 ...

  10. Portainer 基本功能介紹之升級映像檔並更新 Container

    文档地址:https://www.asustor.com/zh-tw/online/College_topic?topic=145#dpt7