• 配平化学方程式的C++代码实现


    配平化学方程式的C++代码实现

    纪念一下我今天写过了 20171006。

    (去年的这个时候我就有了这个大胆的想法, 当时的思路是:字符串处理->暴力搜系数,可是太年轻写不对,我那会还是个只会模拟的孩子啊,(现在也是))

    主要思路:

    先做字符串处理,把每个物质的的每种原子数都找出来,

    然后利用每种原子的守恒 关于系数 列出方程组 进行求解 (化合价好像不太现实,我化学不好)

    先说方程的解法,

    解线性方程组当然是要用高斯消元了。

    (不了解高斯消元 ? 度娘图解链接  luogu模板题)

    #include<bits/stdc++.h>
    using namespace std;
    double M[105][105];
    int N;
    inline bool Gauss()
    {
        for(int k=1;k<=N;k++){
            double maxm=-1;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-7)
                return false;
            if(maxi-k)
                for(int j=1;j<=N+1;j++)
                    swap(M[maxi][j],M[k][j]);
            double tmp=M[k][k];
            for(int j=1;j<=N+1;j++)
                M[k][j]/=tmp;
            for(int i=k-1?1:2;i<=N;i++){
                if(i==k)continue;
                double tmp=M[i][k];
                for(int j=1;j<=N+1;j++)
                    M[i][j]-=tmp*M[k][j];
            }
        }
        return true;
    }
    int main()
    {
        scanf("%d",&N);
        for(int i=1;i<=N;i++)
            for(int j=1;j<=N+1;j++)
                scanf("%lf",&M[i][j]);
        if(Gauss())
            for(int i=1;i<=N;i++)
                printf("%.2lf
    ",M[i][N+1]);
        else    printf("No Solution");
        return 0;
    }
    高斯消元朴素模板

    我们在处理字符串的过程中, 直接把对应系数 放到矩阵中 ,

    考虑一个问题 , 化学方程式的系数是可以按比例变化的,所以这个方程组应该是无穷解,

    我们的方法是 设其中的一个系数为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=1;
            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==1)printf("%d
    ",a);
            else printf("%d/%d
    ",a,b);
        };
    };        
    inline frac Abs(frac x){
        int p=x.a>0?x.a:-x.a,q=x.b>0?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==0?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=1;
            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==1)printf("%d
    ",a);
            else printf("%d/%d
    ",a,b);
        };
    };        
    inline frac Abs(frac x){
        int p=x.a>0?x.a:-x.a,q=x.b>0?x.b:-x.b;
        return (frac){p,q};
    }                                            
    char s[55];
    int fun[55][55];
    int Map[27][27];                        //手动MAP 
    frac M[55][55];                            //求解矩阵
    frac ans[55];                            //
    int Ans[55];                            //整数解 
    int cnt,c1,c2,flag=1,N,K;                //cnt数元素,c1数反应物,c2总数 (未知数的数量) 
    char mat[55][55];                        //存储物质的名称 
    void print(){
        printf("%d %d
    ",N,K);
        for(int i=1;i<=K;i++){
            for(int j=1;j<=N+1;j++)
                printf("%d ",M[i][j].a);
            printf("
    ");
        }
        printf("
    ");
    }
    inline int getint(int pos){                //读数 
        pos++;
        if(s[pos]>='a'&&s[pos]<='z')pos++;
        if(s[pos]<'0'||s[pos]>'9')return 1;                                //没数就是1 
        else {
            int x=0;
            while(s[pos]>='0'&&s[pos]<='9')x=x*10+s[pos]-'0',pos++;        //读元素后面的数字 
            return x;
        }
    }
    inline void scan(int l,int r){             //处理物质    
        c2++;
        for(int i=0;i<=r-l;i++)mat[c2][i]=s[l+i];        //存下元素的名字
        if(flag==1)c1++;                                //统计一下反应物数量
        int tmp=1;                                        //tmp是小括号倍数 
        for(int i=l;i<=r;i++){
            if(s[i]==')')tmp=1;                                                        
            if(s[i]=='('){
                int j=i+1;while(s[j]!=')')j++;            //找这个括号的范围 
                tmp=getint(j);                            //读")"右边的数字 
            }
            if(s[i]>='A'&&s[i]<='Z'){                    //发现元素 
                int x=s[i]-'A'+1,y=0;
                if(s[i+1]>='a'&&s[i]<='z')                //看一眼是一个字母的还是两个的 
                    y=s[i+1]-'a'+1;
                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;                                    //令最后一个解为1
        for(int i=1;i<=cnt;i++){
            for(int j=1;j<=c2;j++)
                M[i][j]=fun[i][j];
        }
        for(int i=1;i<=cnt;i++)
            M[i][c2].a=-M[i][c2].a;            //移到常数 
        //高斯消元过程 
        N=c2-1,K=cnt;
        for(int k=1;k<=N;k++){
            frac maxm=(frac){-1,1};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){0,1})
                return false;
            if(maxi!=k)
                for(int j=1;j<=N+1;j++){
                    swap(M[k][j],M[maxi][j]);
                }
            frac tmp=M[k][k];
            for(int j=1;j<=N+1;j++)
                M[k][j]=M[k][j]/tmp;
            for(int i=k-1?1:2;i<=K;i++){
                if(i==k)continue;
                frac tmp=M[i][k];
                for(int j=1;j<=N+1;j++)
                    M[i][j]=M[i][j]-tmp*M[k][j];
            }
        }
        return true;
    }
    int main()
    {
    //    printf("Chemical Equation Balancer
    ");
    //    printf("
    Enter the chemical equation:
    ");
        scanf("%s",s);
        int lst=0;
        for(int i=1;i<strlen(s);i++){
            if(i==strlen(s)-1)scan(lst,i);                    
            if(s[i]=='+'||s[i]=='=')scan(lst,i-1),lst=i+1;     
            if(s[i]=='=')flag=-1;                            //等号后面的系数变负 
        }
        if(Solve())
            for(int i=1;i<=c2-1;i++)
                ans[i]=M[i][N+1];
        else printf("No Solution");
        int tmp=lcm(ans[1].b,ans[2].b);
        for(int i=3;i<=c2;i++)tmp=lcm(tmp,ans[i].b);
        for(int i=1;i<=c2;i++)Ans[i]=ans[i].a*tmp/ans[i].b;    //取分母Lcm,把分数变整数 
        for(int i=1;i<=c2;i++)
        {
            if(Ans[i]>1)printf("%d",Ans[i]);
            for(int j=0;j<strlen(mat[i]);j++)
                printf("%c",mat[i][j]);
            if(i==c2)return 0;
            else if(i==c1)printf("=");
            else printf("+");
        }
    }

    luogu提交链接

    可能会被有的方程式卡住,欢迎Hack,欢迎Debug

  • 相关阅读:
    Python循环语句
    Python判断语句
    MySQL的基本操作汇总
    Python函数、类
    Python字典、集合
    Python列表、元组
    python数据类型--数字、字符串
    Python基础认识
    搭建Python独立虚拟环境
    Python包管理工具
  • 原文地址:https://www.cnblogs.com/Elfish/p/7631603.html
Copyright © 2020-2023  润新知