ICPC North Central NA Contest 2017 - A Stoichiometry(高斯消元)

题目链接

​You have landed a lucrative contract with Amalgamated Chemical Manufacturing (ACM), to help their chemists with stoichiometry. Stoichiometry is the calculation of reactants and products in chemical reactions, based on the law of conservation of mass, which states that the total mass of the reactants equals the total mass of the products. The relations among quantities of reactants and products typically form a ratio of positive integers. If the amounts of the separate reactants are known, then the amount of the product can be calculated, and vice-versa. The relationship of reactants to products can be described using a soichiometric equation such as:
在这里插入图片描述
(输入输出请参考原文)

1.题目大意:输入化学方程式配平,如果有多组结果,就输出系数最小的整数解。
输入格式为:每行输入一种物质。以"+?“开头,”+“代表在等式左边,该物质初始系数是”?",第二个字符串代表接下来有几个元素,然后是每个元素,每个元素后面跟它在这个物质中的个数,水就是H2O1。以"0 0"代表输入结束

2.比赛时根本没有看这道题,实际上就主要考察高斯消元的应用。但是这个题目还有两个比较难搞的地方,一个是输入,另外一个是含有一个未知变元并求出一组最小整数解

3.首先我们解决输入:使用getline读入一行字符串,接着使用stringstream分割空格得到每个字符串并记录个数,接着我们通过开头的字符串确定初始系数是正还是负,并得到系数的大小。然后就是处理每个元素了。因为元素可能有多个字母如“Ag”,那么我们使用string存元素,然后用map给每个元素赋值一个ID。接下来是重点了,如何得到方程组呢?对第一个样例,我们手推不难发现方程组中未知数个数对应物质个数,这个我们之前有记录。那么如何将方程组对号入座,总的来说是根据元素ID确定行,根据第几个物质确定列。具体的:对于每个元素,我们都可以列一个方程组,那么每当我们处理一个元素,就根据ID判断它是哪一个方程,接着根据后面跟的个数乘以系数,col代表这是第几个物质,我们有a[ mp[str[i]] ][col]+=(double)y;注意这里要+=,因为像CHOOH这样的物质,O和H在同一物质多次出现,那么就累加到同一个方程的同一个未知数上

4.接下来,主要算法是高斯消元,可以网上自行学习,也可以参考我的博客。但是需要注意方程组有可能存在一个通解,而且一定是最后一行(该行打印出来全为0),因此我们直接令该通解为1,接下来就是如何将一组浮点数通分化为最小的一组整数解

5.最后,对于下列一组浮点数解:
在这里插入图片描述
对于每个非整数的浮点数从k=2开始不断乘,k++,直到将该浮点数化为整数,那么返回k,对于整数的话直接返回1,我们得到:
在这里插入图片描述
接着我们求这一组数的最小公倍数,再将每个浮点数乘以这个最小公倍数即可得到最小的整数解
在这里插入图片描述
6.本来我是只会高斯-约旦消元的,也就是把增广矩阵化为行最简矩阵,但是超时了,无奈我又去学习了一下正常的高斯消元,跑的很快,事实证明了高斯-约旦消元效率太低,以后尽量写高斯消元
在这里插入图片描述

代码:

#include <string>
#include <math.h>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <tr1/unordered_map>
using namespace std;
const double esp=1e-8;
tr1::unordered_map<string,int> mp;
double a[1005][1005];
int b[1005];
string str[105];
int num,col;      //num是元素个数,col是方程个数

int gcd(int n,int m){
    return m==0?n:gcd(m,n%m);
}

int lcm(int n,int m){
    return n/gcd(n,m)*m;
}

void getID(string s){          //ID化字符串
    if(!mp.count(s)){
        mp[s]=++num;
    }
}

int getNumber(string s){       //字符串转换为数字
    int ans=0;
    for(int i=0;i<s.size();i++){
        if(s[i]>'0' && s[i]<'9') ans=ans*10+s[i]-'0';
    }
    return ans;
}

int check(double p){   //判断一个浮点数需要最小乘以多大的整数变成整数
    int q=(int)p;
    if(fabs(p-q)<esp) return 1;
    else{
        int k=1;
        double res=p;
        while(fabs(res-q)>esp){
            k++;
            res=p*k;
            q=(int)res;
        }
        return k;
    }
}

void solve(){  			//求最小整数解
    for(int i=1;i<=col;i++){
        b[i]=check(fabs(a[i][col+1]));
    }
    int ans=b[1];
    for(int i=2;i<=col;i++){
        ans=lcm(ans,b[i]);
    }
    for(int i=1;i<=col;i++) a[i][col+1]*=ans;
}

/*打印矩阵
bool judge(double p){
    int q=(int)p;
    if(fabs(p-q)<esp) return 1;
    return 0;
}

void print(){
    for(int i=1;i<=col;i++){
        for(int j=1;j<=col+1;j++)
            if(judge(a[i][j])) printf("%.0lf ",a[i][j]);
            else printf("%.1lf ",a[i][j]);
        printf("\n");
    }
    printf("\n");
}*/

void gauss(int n) {
    for(int i=1;i<n;i++){
        int temp=i;
        for(int j=i+1;j<=n;j++)
            if(fabs(a[j][i])>fabs(a[temp][i])) temp=j;
        if(fabs(a[temp][i])<esp) return;
        if(temp!=i){
            for(int j=i;j<=n+1;j++)
                swap(a[i][j],a[temp][j]);
        }
        double p;
        for(int j=i+1;j<=n;j++) {
            p=a[j][i]/a[i][i];
            for(int k=i;k<=n+1;k++)
                a[j][k]-=a[i][k]*p;
        }
    }
    if(fabs(a[n][n])<esp){  		//如果有变元,那么一定是在最后一行
        a[n][n]=a[n][n+1]=1;
    }

    for(int i=n;i>=1;i--){
        for(int j=i+1;j<=n;j++)
            a[i][n+1]-=a[i][j]*a[j][n+1];
        a[i][n+1]/=a[i][i];
    }
}


int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    string s,buf;
    num=col=0;  
    mp.clear();
    memset(a,0,sizeof a);
    while(getline(cin,s)){
        stringstream ss(s);
        int tot=0;
        while(ss>>buf){
            str[++tot]=buf;
        }
        if(tot==2 && str[1]=="0" && str[2]=="0") break;
        col++;
        char op=str[1][0];
        int cnt=getNumber(str[1]);
        if(op=='+'){
            for(int i=3;i<=tot;i+=2){
                getID(str[i]);
                int y=getNumber(str[i+1]);
                y*=cnt;
                a[mp[str[i]]][col]+=(double)y;
            }
        }else{
            cnt=0-cnt;
            for(int i=3;i<=tot;i+=2){
                getID(str[i]);
                int y=getNumber(str[i+1]);
                y*=cnt;
                a[mp[str[i]]][col]+=(double)y;
            }
        }
    }
    gauss(col);
    //print();
    solve();
    for(int i=1;i<=col;i++)
        printf("%.0lf ",a[i][col+1]);
    return 0;
}


发布了128 篇原创文章 · 获赞 7 · 访问量 5264

猜你喜欢

转载自blog.csdn.net/qq_44691917/article/details/104601827