配平化学方程式的C++代码实现
配平化学方程式的C++代码实现
纪念一下我今天写过了 20171006。
(去年的这个时候我就有了这个大胆的想法, 当时的思路是:字符串处理->暴力搜系数,可是太年轻写不对,我那会还是个只会模拟的孩子啊,(现在也是))
主要思路:
先做字符串处理,把每个物质的的每种原子数都找出来,
然后利用每种原子的守恒 关于系数 列出方程组 进行求解 (化合价好像不太现实,我化学不好)
先说方程的解法,
解线性方程组当然是要用高斯消元了。
#include<bits/stdc++.h>
using namespace std;
double M[][];
int N;
inline bool Gauss()
{
for(int k=;k<=N;k++){
double maxm=-;int maxi;
for(int i=k;i<=N;i++)
if(maxm<fabs(M[i][k]))
maxm=fabs(M[i][k]),maxi=i;
if(fabs(maxm)<1e-)
return false;
if(maxi-k)
for(int j=;j<=N+;j++)
swap(M[maxi][j],M[k][j]);
double tmp=M[k][k];
for(int j=;j<=N+;j++)
M[k][j]/=tmp;
for(int i=k-?:;i<=N;i++){
if(i==k)continue;
double tmp=M[i][k];
for(int j=;j<=N+;j++)
M[i][j]-=tmp*M[k][j];
}
}
return true;
}
int main()
{
scanf("%d",&N);
for(int i=;i<=N;i++)
for(int j=;j<=N+;j++)
scanf("%lf",&M[i][j]);
if(Gauss())
for(int i=;i<=N;i++)
printf("%.2lf\n",M[i][N+]);
else printf("No Solution");
return ;
}
高斯消元朴素模板
我们在处理字符串的过程中, 直接把对应系数 放到矩阵中 ,
考虑一个问题 , 化学方程式的系数是可以按比例变化的,所以这个方程组应该是无穷解,
我们的方法是 设其中的一个系数为1 先解方程 ,然后把解出的分数通分 ,就可以得到最简整数解。
举个例子
C16H18O9+O2=CO2+H2O 设系数分别是 x1,x2,x3,x4 ,三个原子守恒方程 :
C : x1*16+x2*0-x3*1-x4*0=0
H : x1*18+x2*0-x3*0-x4*2=0
O : x1*9+x2*2-x3*2-x4*1=0
设最后一个也就是x4为1 ,那么方程就变成了
16 0 -1 0
18 0 0 2
9 2 -2 1
现在就可以直接套高斯消元模板了。
对了 还有 ,因为要出分数,我们需要一个分数类模板
struct frac{ //分数类
int a,b;
void reduce(){
int x=gcd(a,b);
a/=x,b/=x;
};
frac operator = (int x){
a=x,b=;
return *this;
};
frac operator = (const frac x){
a=x.a,b=x.b;
reduce();
return *this;
};
frac operator + (const frac x){
return (frac){b*x.a+a*x.b,b*x.b};
};
frac operator - (const frac x){
return (frac){a*x.b-b*x.a,b*x.b};
};
frac operator * (const frac x){
return (frac){a*x.a,b*x.b};
};
frac operator / (const frac x){
return (frac){a*x.b,b*x.a};
};
bool operator < (const frac x){
return a*x.b<b*x.a;
};
bool operator == (const frac x){
return a*x.b==b*x.a;
};
void print(){
if(b==)printf("%d\n",a);
else printf("%d/%d\n",a,b);
};
};
inline frac Abs(frac x){
int p=x.a>?x.a:-x.a,q=x.b>?x.b:-x.b;
return (frac){p,q};
}
分数类模板
实现过程小问题 :
1.要记好每个物质的名字输出的时候用
2.元素的名字拿Map这种东西判重,我是开了一个26*26数组手动判重
3.转类型赋值可能会出现问题,我可是调了好久,心态爆炸
4.把系数放进矩阵的时候有很多小细节 :比如说存储的位置、括号的倍数、正负什么的
5.高斯消元里面那个交换操作很关键。而且这个矩阵不一定是正方形,注意写在循环里的长和宽边界。
下面是完全版代码
/*
Chemical Equation Balancer
HiJ1m 2017.10.6
*/
#include<bits/stdc++.h>
using namespace std;
inline int gcd(int x,int y){
return x%y==?y:gcd(y,x%y);
}
inline int lcm(int x,int y){
return x*y/gcd(x,y);
}
struct frac{ //分数类
int a,b;
void reduce(){
int x=gcd(a,b);
a/=x,b/=x;
};
frac operator = (int x){
a=x,b=;
return *this;
};
frac operator = (const frac x){
a=x.a,b=x.b;
reduce();
return *this;
};
frac operator + (const frac x){
return (frac){b*x.a+a*x.b,b*x.b};
};
frac operator - (const frac x){
return (frac){a*x.b-b*x.a,b*x.b};
};
frac operator * (const frac x){
return (frac){a*x.a,b*x.b};
};
frac operator / (const frac x){
return (frac){a*x.b,b*x.a};
};
bool operator < (const frac x){
return a*x.b<b*x.a;
};
bool operator == (const frac x){
return a*x.b==b*x.a;
};
void print(){
if(b==)printf("%d\n",a);
else printf("%d/%d\n",a,b);
};
};
inline frac Abs(frac x){
int p=x.a>?x.a:-x.a,q=x.b>?x.b:-x.b;
return (frac){p,q};
}
char s[];
int fun[][];
int Map[][]; //手动MAP
frac M[][]; //求解矩阵
frac ans[]; //解
int Ans[]; //整数解
int cnt,c1,c2,flag=,N,K; //cnt数元素,c1数反应物,c2总数 (未知数的数量)
char mat[][]; //存储物质的名称
void print(){
printf("%d %d\n",N,K);
for(int i=;i<=K;i++){
for(int j=;j<=N+;j++)
printf("%d ",M[i][j].a);
printf("\n");
}
printf("\n");
}
inline int getint(int pos){ //读数
pos++;
if(s[pos]>='a'&&s[pos]<='z')pos++;
if(s[pos]<''||s[pos]>'')return ; //没数就是1
else {
int x=;
while(s[pos]>=''&&s[pos]<='')x=x*+s[pos]-'',pos++; //读元素后面的数字
return x;
}
}
inline void scan(int l,int r){ //处理物质
c2++;
for(int i=;i<=r-l;i++)mat[c2][i]=s[l+i]; //存下元素的名字
if(flag==)c1++; //统计一下反应物数量
int tmp=; //tmp是小括号倍数
for(int i=l;i<=r;i++){
if(s[i]==')')tmp=;
if(s[i]=='('){
int j=i+;while(s[j]!=')')j++; //找这个括号的范围
tmp=getint(j); //读")"右边的数字
}
if(s[i]>='A'&&s[i]<='Z'){ //发现元素
int x=s[i]-'A'+,y=;
if(s[i+]>='a'&&s[i]<='z') //看一眼是一个字母的还是两个的
y=s[i+]-'a'+;
if(!Map[x][y])Map[x][y]=++cnt; //判重
fun[Map[x][y]][c2]+=flag*getint(i)*tmp; //把这个物质里的这种元素数量放进矩阵里,坐标(map[x][y],c2)
}
}
}
inline bool Solve(){ //解方程 (矩阵 高cnt,宽c2+1,c2+1列常数全0)
ans[c2]=; //令最后一个解为1
for(int i=;i<=cnt;i++){
for(int j=;j<=c2;j++)
M[i][j]=fun[i][j];
}
for(int i=;i<=cnt;i++)
M[i][c2].a=-M[i][c2].a; //移到常数
//高斯消元过程
N=c2-,K=cnt;
for(int k=;k<=N;k++){
frac maxm=(frac){-,};int maxi;
for(int i=k;i<=K;i++)
if(maxm<Abs(M[i][k]))
maxm=Abs(M[i][k]),maxi=i;
if(maxm==(frac){,})
return false;
if(maxi!=k)
for(int j=;j<=N+;j++){
swap(M[k][j],M[maxi][j]);
}
frac tmp=M[k][k];
for(int j=;j<=N+;j++)
M[k][j]=M[k][j]/tmp;
for(int i=k-?:;i<=K;i++){
if(i==k)continue;
frac tmp=M[i][k];
for(int j=;j<=N+;j++)
M[i][j]=M[i][j]-tmp*M[k][j];
}
}
return true;
}
int main()
{
// printf("Chemical Equation Balancer\n");
// printf("\nEnter the chemical equation:\n");
scanf("%s",s);
int lst=;
for(int i=;i<strlen(s);i++){
if(i==strlen(s)-)scan(lst,i);
if(s[i]=='+'||s[i]=='=')scan(lst,i-),lst=i+;
if(s[i]=='=')flag=-; //等号后面的系数变负
}
if(Solve())
for(int i=;i<=c2-;i++)
ans[i]=M[i][N+];
else printf("No Solution");
int tmp=lcm(ans[].b,ans[].b);
for(int i=;i<=c2;i++)tmp=lcm(tmp,ans[i].b);
for(int i=;i<=c2;i++)Ans[i]=ans[i].a*tmp/ans[i].b; //取分母Lcm,把分数变整数
for(int i=;i<=c2;i++)
{
if(Ans[i]>)printf("%d",Ans[i]);
for(int j=;j<strlen(mat[i]);j++)
printf("%c",mat[i][j]);
if(i==c2)return ;
else if(i==c1)printf("=");
else printf("+");
}
}
可能会被有的方程式卡住,欢迎Hack,欢迎Debug
配平化学方程式的C++代码实现的更多相关文章
- Python趣用—配平化学方程式
不知不觉已经毕业多年了,不知道大家是否还记得怎么配平化学方程式呢?反正小编我是已经记不太清了,所以今天的文章除了分享如何用python配平化学方程式,顺带着还会复习 一些化学方程式的知识,希望广大化学 ...
- [C++] 配平化学方程式算法的封装
有人已经实现了配平的方法,在此不再重复介绍. https://www.cnblogs.com/Elfish/p/7631603.html 但是,上述的方法所提供的代码还是存在着问题,需要进一步修改. ...
- .net平台下对C#代码的编译
最近赶项目忽然想到一个问题,那就是在 .Net平台下的C#代码是怎么从源代码到机器可以识别的电脑的(只怪自己上学不好好读书,现在又要重补一遍了!!!) 话不多说直接上调研结果: 预习知识: 1: IL ...
- 【23.91%】【hdu 4694】Important Sisters("支NMLGB配树"后记)(支配树代码详解)
Time Limit: 7000/7000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission( ...
- 【HiJ1m】在NOIP2017前写过的有用的东西汇总
http://www.cnblogs.com/Elfish/p/7544623.html 高级树状数组 http://www.cnblogs.com/Elfish/p/7554420.html BST ...
- CCF-CSP题解 201912-3 化学方程式
判断化学方程式是否配平. 字符串处理. 有点编译原理递归下降法的感觉. 考场源码,比较粗糙. // INFO BEGIN // // User = 201911513451(陶杨) // Group ...
- Checkstyle:整洁你的代码
内容 Checkstyle简介 下载 Checkstyle的几种使用方式 1) 与Ant结合使用 2) 通过CLI使用 3)在IDE上使用插件 4)在Maven上使用插件 Checkstyle配置 配 ...
- 如何解决代码中if…else 过多的问题
前言 if...else 是所有高级编程语言都有的必备功能.但现实中的代码往往存在着过多的 if...else.虽然 if...else 是必须的,但滥用 if...else 会对代码的可读性.可维护 ...
- WIFI智能配网 - SmartConfig
要开始IoT项目的第一步是什么?当然不是硬件,而是硬件与硬件的连接!即使有各种各样的通信协议没有好的连接方式绝对不行.那外设上没有的屏幕,没有键盘怎末输入密码怎末选择网络?对,这就是WIFI模块最重要 ...
随机推荐
- 怎样给你的Android 安装文件(APK)瘦身
本文源地址:怎样给你的Android 安装文件(APK)瘦身 Android的apk文件越来越大了这已经是一个不争的事实. 在Android 还是最初版本号的时候,一个app的apk文件大小也还仅仅有 ...
- Window下UDP(socket)接和收数据案例
配置QT的环境变量,这台电脑à属性à高级系统设置à高级à环境变量à系统变量àpathàC:\Qt\Qt5.3.0\5.3\mingw482_32\bin;C:\Qt\Qt5.3.0\Tools\ ...
- iOS 代码方式设置按钮标题、图片的偏移
button.imageEdgeInsets = UIEdgeInsetsMake(0,1 , 2, 3); button.titleEdgeInsets = UIEdgeInsetsMake(0,1 ...
- luogu1070 道路游戏 单调队列
题目大意 小新正在玩一个简单的电脑游戏. 游戏中有一条环形马路,马路上有 nn 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接.小新以某个机器人工厂为起点,按顺时针顺序依次将这 nn 个机器人工 ...
- Android入门之文件系统操作(一)简单的文件浏览器
版权声明:本文为博主原创文章,未经博主允许不得转载. import java.io.File; import java.util.*; import android.app.Activit ...
- P1052 过河(状态压缩)
P1052 过河(状态压缩) 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把 ...
- Python入门 来点栗子
查天气(1) http://wthrcdn.etouch.cn/weather_mini?citykey=101280804 http://wthrcdn.etouch.cn/WeatherApi?c ...
- javascript必须知道的知识要点(二)
该文章不详细叙述各知识要点的具体内容,仅把要点列出来,供大家学习的时候参照,或者检测自己是否熟练掌握了javascript,清楚各个部分的内容. 内建对象可划分为数据封装类对象.工具类对象.错误类对象 ...
- python-day3 元组(tuple),列表(list),字典(dict)
1.元组 tuple 有序数据,元组数据不可更改,若元组中有列表,可更改元组中的列表值里的值 元组中以","分开,若只有一个值就不是元组 包含各种数据类型 索引取值:t(2,0.0 ...
- PHP CURL抓取网页 simple_html_dom类
抓取网页数据后 数据录入到discuz中 <?php include('simple_html_dom.php'); function urlText(){ $url = 'http://www ...