「浙江理工大学ACM入队200题系列」问题 F: 零基础学C/C++39——求方程的解
本题是浙江理工大学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——求方程的解的更多相关文章
- 「浙江理工大学ACM入队200题系列」问题 E: 零基础学C/C++78——求奇数的乘积
本题是浙江理工大学ACM入队200题第八套中的E题 我们先来看一下这题的题面. 题面 输入 输入数据包含多个测试实例,每个测试实例占一行,每行的第一个数为n,表示本组数据一共有n个,接着是n个整数,你 ...
- 「浙江理工大学ACM入队200题系列」问题 B: 零基础学C/C++12——求平均值
本题是浙江理工大学ACM入队200题第二套中的B题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...
- 「浙江理工大学ACM入队200题系列」问题 L: 零基础学C/C++85——完美数
本题是浙江理工大学ACM入队200题第八套中的L题 我们先来看一下这题的题面. 题面 题目描述 任何一个自然数的约数中都有1和它本身,我们把小于它本身的因数叫做这个自然数的真约数. 如6的所有真约数是 ...
- 「浙江理工大学ACM入队200题系列」问题 K: 零基础学C/C++84——奇偶ASCII值判断
本题是浙江理工大学ACM入队200题第八套中的K题 我们先来看一下这题的题面. 题面 题目描述 任意输入一个字符,判断其ASCII是否是奇数,若是,输出YES,否则,输出NO; 例如,字符A的ASCI ...
- 「浙江理工大学ACM入队200题系列」问题 J: 零基础学C/C++83——宁宁的奥数路
本题是浙江理工大学ACM入队200题第八套中的J题 我们先来看一下这题的题面. 题面 题目描述 宁宁参加奥数班,他遇到的第一个问题是这样的:口口口+口口口=口口口,宁宁需要将1~9 九个数分别填进对应 ...
- 「浙江理工大学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项和.注意: ...
- 「浙江理工大学ACM入队200题系列」问题 A: 零基础学C/C++34—— 3个数比较大小(冒泡排序与选择排序算法)
本题是浙江理工大学ACM入队200题第四套中的A题,同时给出了冒泡排序和选择排序算法 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习 ...
- 「浙江理工大学ACM入队200题系列」问题 H: 零基础学C/C++18——三位数反转
本题是浙江理工大学ACM入队200题第二套中的H题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...
- [Python] 文科生零基础学编程系列二——数据类型、变量、常量的基础概念
上一篇:[Python] 文科生零基础学编程系列--对象.集合.属性.方法的基本定义 下一篇: (仍先以最简单的Excel的VBA为例,语法与Python不同,但概念和逻辑需要理解透彻) p.p1 { ...
随机推荐
- Little Girl and Problem on Trees
题意 给定一棵无边权的树,最多只有一个点度数超过2,有两种操作 1)(0 u x d)将距离u节点d距离之内的节点的值加上x 2)(1 u)询问u节点的值 n<=100000,q<=100 ...
- MySQL源码分析之SQL函数执行
1.MySQL中执行一条SQL的总体流程 2.SQL函数执行过程 1.MySQL中执行一条SQL的总体流程 一条包含函数的SQL语句,在mysql中会经过: 客户端发送,服务器连接,语法解析,语句执行 ...
- 一款类似B站的开源弹幕播放器,太酷了
今天小编推荐一款开源的弹幕视频播放器,由Typescript加Sass编写,无任何第三方运行时依赖,Gzip大小只有21KB,兼容IE11,支持SSR,支持直播.该播放器高度可定制,所有图标.按钮.色 ...
- Python代码用在这些地方,其实1行就够了!
摘要:都说 Python 简单快捷,那本篇博客就为大家带来一些实用的 Python 技巧,而且仅需要 1 行代码,就可以解决一些小问题. 本文分享自华为云社区<你猜 1 行Python代码能干什 ...
- Postman中的Pre-request Scrip详解
Postman中的Pre-request Scrip详解 一.Pre-request Scrip的简介 1.Pre-request Script是在请求发送之前需要执行的代码片段: 2.请求参数中包含 ...
- 图解Kubernetes的Pod核心资源-来白嫖啊
目录 一.Pod定义 二.Pod入门yaml描述文件 三.共享NetworkNamespace 四.共享PID 五.容器生命周期 六.初始化容器 6.1.简介 6.2.与普通容器的区别 6.3.实验 ...
- Java代码审计之不安全的Java代码
Java代码审计之不安全的Java代码 在打靶场的同时,需要想一下如果你是开发人员你会怎样去防御这种漏洞,而作为攻击方你又怎么去绕过开发人员的防御. 环境搭建 https://github.com ...
- 异步编程promise
异步编程发展 异步编程经历了 callback.promise.async/await.generator四个阶段,其中promise和async/await使用最为频繁,而generator因为语法 ...
- 4、StringBuilder类
StringBuilder类 一个可变的字符序列,此类提供一个与StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全). 该类被设计用作 StringBuf ...
- Elastic:使用Postman来访问Elastic Stack
转载自:https://elasticstack.blog.csdn.net/article/details/104982536 官方链接地址:https://www.elastic.co/guide ...