题目背景
NCL是一家专门从事计算器改良与升级的实验室,最近该实验室收到了某公司所委托的一个任务:需要在该公司某型号的计算器上加上解一元一次方程的功能。实验室将这个任务交给了一个刚进入的新手ZL先生。
题目描述
为了很好的完成这个任务,ZLZL先生首先研究了一些一元一次方程的实例:
4+3x=84+3x=8
6a-5+1=2-2a6a−5+1=2−2a
-5+12y=0−5+12y=0
ZLZL先生被主管告之,在计算器上键入的一个一元一次方程中,只包含整数、小写字母及+、-、=这三个数学符号(当然,符号“-”既可作减号,也可作负号)。方程中并没有括号,也没有除号,方程中的字母表示未知数。
你可假设对键入的方程的正确性的判断是由另一个程序员在做,或者说可认为键入的一元一次方程均为合法的,且有唯一实数解。
输入格式
一个一元一次方程。
输出格式
解方程的结果(精确至小数点后三位)。
代码
/*
* @Author: Gehrychiang
* @Date: 2019-10-13 17:32:20
* @Last Modified by: Gehrychiang
* @Last Modified time: 2019-10-15 19:48:46
* @Email:gehrychiang@ailiyun.com
* @Website:www.yilantingfeng.site
*/
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
using namespace std;
int main(int argc, char const *argv[])
{
int tmp=0;
char input [100];
char process [100];
char left [50]={0};
char right [50]={0};
char tmptmp [100]={0};
char unknown;
int leftlength,rightlength;
char *p;
char *q;
int label=0;
int changshu=0;
int xishu=0;
int i=0;
gets(input);
// cout<<input;
for (int i = 0; input[i]||(input[i]=='0'&&input[i+1]!='0'); ++i)
{
if ((int)input[i]>=97&&(int)input[i]<=122)
{
unknown=input[i];
break;
}
}
strcpy(process,input);
for (int i = 0; process[i]!='='; ++i)
{
left[i]=process[i];
tmp=i+2;
}
for (int i = tmp;process[i]||((process[i]=='0')&&(process[i-1]=='+'||process[i-1]=='='||process[i-1]=='-')) ; ++i)
{
right[i-tmp]=process[i];
}
tmp=0;
//test
//cout<<left<<endl;
//cout<<right<<endl;
leftlength=strlen(left);
rightlength=strlen(right);
for ( p=right; p-right<=rightlength ; ++p)
{
if (p==right&&*p!='-')
{
label=1;
i=0;
}
if (*p=='-')
{
changshu=changshu+label*atoi(tmptmp);
memset(tmptmp, 0, sizeof tmptmp);
label=-1;
i=0;
++p;
}
if (*p=='+')
{
changshu=changshu+label*atoi(tmptmp);
memset(tmptmp, 0, sizeof tmptmp);
label=1;
i=0;
++p;
}
if ((int)*p>=48&&(int)*p<=57)
{
tmptmp[i]=*p;
++i;
}
if (*p==unknown)
{
if (atoi(tmptmp)==0)
{
xishu=xishu-label*1;
}
else
{
xishu=xishu-label*atoi(tmptmp);
}
memset(tmptmp, 0, sizeof tmptmp);
}
if (*p=='\0')
{
changshu=changshu+label*atoi(tmptmp);
}
}
//cout<<changshu<<endl<<xishu<<endl;
memset(tmptmp, 0, sizeof tmptmp);
for ( p=left; p-left<=leftlength ; ++p)
{
if (p==left&&*p!='-')
{
label=1;
i=0;
}
if (*p=='-')
{
changshu=changshu-label*atoi(tmptmp);
memset(tmptmp, 0, sizeof tmptmp);
label=-1;
i=0;
++p;
}
if (*p=='+')
{
changshu=changshu-label*atoi(tmptmp);
memset(tmptmp, 0, sizeof tmptmp);
label=1;
i=0;
++p;
}
if ((int)*p>=48&&(int)*p<=57)
{
tmptmp[i]=*p;
++i;
}
if (*p==unknown)
{
if (atoi(tmptmp)==0)
{
xishu=xishu+label*1;
}
else
{
xishu=xishu+label*atoi(tmptmp);
}
memset(tmptmp, 0, sizeof tmptmp);
}
if (*p=='\0')
{
changshu=changshu-label*atoi(tmptmp);
}
}
//cout<<xishu<<endl<<changshu<<endl;
double result=(double)changshu/xishu;
if (result==-0)
{
result=0;
}
printf("%c=%.3lf",unknown,result);
return 0;
}
心得
好题目是好题目,解决问题也是的确在提升自己。
先说题目本身,看似一个很简单的甚至一个小学初中生都会做的一条解一元一次方程的题目在洛谷上却只有30%,我想已经能够从一定程度上说明这条题目的难度。
从解决问题出发,本蒟蒻的思路其实根本上趋同与我们小时候解方程时的手法
等式两边先合并同类项;左右移项变号;去系数得解。
但是在具体实践上却并不如所想的那么简单。
我认为这就是将人脑的模式化思维向机器的格式化思维转变的一个过程。
我在这里就采用了一种比较慢但是个人认为具有较强通用性与操作性的一种方式——字符串遍历。
先将输入的字符串以 "=" 隔开
以等式左边(左侧字符串)为例,利用指针在遍历过程中遇到符号就在label变量中标记,再挨个将读取到的数字存入一个临时数组,直到遇到下一个符号或字母或字符串结尾。此时将临时数组转换为临时变量放入常数或系数中,最后将临时数组释放,开始下一轮循环操作。
最后说一些教训
第一是C++的老毛病 0/(-1)=-0。这一点在今后的数据处理当中需格外注意
第二是方程中系数的问题,我们习惯当未知数系数为1的时候将1省略,所以这一点也需要在代码中有所调整
第三则是数据处理安全性的一个思想,当然我在这个题目中也并没有做的很好,在对字符串处理的时候应该要有备份以供确认,并且gets函数本身也的确不够安全,我之后会专门写一篇文章来讲如何更安全的读取字符串。