P1022 [NOIP2000 普及组] 计算器的改良

一.题目概括

给出一个合法的一元一次方程,求它的解(注意,方程只包含整数、小写字母及 +-= 三个数学符号(当然,符号“-”既可作减号,也可作负号)。方程中没有括号,也没有除号,方程中的字母表示未知数,该方程有唯一解)

输出解方程的结果(精确至小数点后三位)。

二.关键点

1.小学数学

(没有学过解一元一次方程的OIer参考https://zhuanlan.zhihu.com/p/661851542

一元一次方程的计算在小学五年级有教到,可以总结出以下步骤:

去分母(通过两边同时乘以所有分母的公倍数,最好是最小公倍数)
去括号(如果有括号,或去分母后产生括号,那么为了合并同类项先把括号去掉)
移项(把未知数都移到一边,常数都移到另一边)
合并同类项(变成ax=b的形式)
系数化为1(方程两边同时除以a,即x=b/a) 

--引自哔哩哔哩

所以,可以现在纸面上进行筛选和添加

去分母:由于常数全部为整数,所以舍去

去括号:由于无括号,所以舍去

移项:解方程中必须保留(在筛选分类时可以把含有未知数的项移到vector动态数组a,把常数项移到vector动态数组b,就可以被视为移项)

合并同类项:解方程中必须保留

系数化为一:iomanip库函数setprecision(n)可以做到保留n位的效果,保留

但是这里还需要添加一些(毕竟电脑不是人脑)

筛选未知数:将未知数筛选出来,遍历一遍数组即可

分割项:将多项式分割成若干单项式之和,这里可以和移项同步进行

筛选左右两边的数字:即把ax=b中a和b挑选出来

所以可以得到大致思路

输入-->筛选未知数-->分割项并移项-->合并同类项-->输出(同时系数化为1)

2.细节
(1).补数字

类似a+3a=4这一类的方程,可以注意到,‘1’被省略了,所以需要特判

(2).变号

我们默认含有未知数的项在等号左边,常数项在右边,所以当一个项在其所属方向异侧移动时,需要变号

(3)溢出问题

选择在输入字符串后面加入一个字符‘&’

三.AC代码

1.解法1:

字符数组做法和字符串类似,不再展示

#include <iostream>
#include <cstring>
#include <vector>
#include <iomanip>//保留三位浮点数库
using namespace std;
string s,tmp;
int ls,lv0,lv1,flag,num_l,num_r;//lv0:左边数字和,lv1:右边数字和
char ch;
vector<string> vec[2];
char find_ch(){
	for(int i=0;i<ls;i++){
		if('a'<=s[i]&&s[i]<='z')return s[i];//找到未知数,返回
	}
}
void seperate(int ls){
	bool flag=false;
	bool flaga=false;
	for(int i=0;i<ls;i++){
		if(s[i]=='=')flaga=true;//左边变成右边
		else if(s[i+1]=='+'||s[i+1]=='-'||s[i+1]=='='||s[i+1]=='&'){
			tmp.push_back(s[i]);
			flag=true;
			if(tmp.find(ch)!=string::npos){//该项找到了未知数
				if(tmp[0]!='+'&&tmp[0]!='-')tmp='+'+tmp;
				if(flaga&&tmp[0]=='-')tmp[0]='+';
				else if(flaga&&tmp[0]=='+')tmp[0]='-';
			}
			else{
				if(tmp[0]!='+'&&tmp[0]!='-')tmp='+'+tmp;
				if(!flaga&&tmp[0]=='-')tmp[0]='+';
				else if(!flaga&&tmp[0]=='+')tmp[0]='-';	
			}
			if(tmp.find(ch)!=string::npos)vec[0].push_back(tmp);
			else vec[1].push_back(tmp);
			tmp.clear();
		}
		else if(flag&&s[i]=='+')flag=false;
		else if(flag&&s[i]=='-'){
			tmp.push_back(s[i]);
			flag=false;
		}
		else tmp.push_back(s[i]);
	}
	return;
}
int main(){
	cin>>s;
	s.push_back('&');//防止溢出,见第20行代码
	ls=s.length();
	ch=find_ch();
	seperate(ls);
	lv0=vec[0].size();
	lv1=vec[1].size();
	for(int i=0;i<lv0;i++){
		string v=vec[0][i];
		int t=0;
		for(int j=0;j<v.length();j++){
			if('0'<=v[j]&&v[j]<='9')t=t*10+(v[j]-'0');//构造数字
			else continue;
		}
		if(v[0]=='-')t*=(-1);//负数变号
		if(t==0)t=1;
		num_l+=t;
	}
	for(int i=0;i<lv1;i++){
		string v=vec[1][i];
		int t=0;
		for(int j=0;j<v.length();j++){
			if('0'<=v[j]&&v[j]<='9')t=t*10+(v[j]-'0');
			else continue;
		}
		if(v[0]=='-')t*=(-1);
		num_r+=t;
	}
	cout<<ch<<'=';
	cout<<fixed<<setprecision(3)<<num_r/(num_l*1.00);//保留三位输出
}

时间:


四.直达链接

https://www.luogu.com.cn/problem/P1022

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值