acwing_5721_化学方程式配平

acwing_5721_化学方程式配平

这是一道T3的题目,操作起来可能有些棘手,但是耐下心来做一遍会有收获的!

下面是对于大佬的题解进行的注释

#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 42;
double mat[N][N];
int n, m;
map<string, int> elei; // map中存放的是某种元素对应的标号(方程式中的每一个元素都有其标号,也就是系数矩阵的行号)
int index;

double eps = 1e-6;

void initMat() {
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            mat[i][j] = 0;
}

void getElement(string str, int &index, int in) {
    // index代表这个方程式中的第几号元素(方程式中的每一个元素都有其标号,也就是系数矩阵的行号),in 代表第几个物质
    str = str + '#'; // 方便对于最后一个物质进行处理
    string ele = "";
    int num = 0;
    bool complete = false;
    for (int i = 0; i < str.size(); i++) {
        char c = str[i];
        if (c >= '0' && c <= '9') {
            // 读到数字应该如何处理
            complete = true;
            num = num * 10 + c - '0';
        } else {
            // 读到字母应该如何处理
            if (complete) {
                // 如果元素已经读取完毕
                if (elei.find(ele) != elei.end()) {
                    // map中有这一个元素
                    mat[elei[ele]][in] = num;
                } else {
                    // map中没有这个元素
                    elei[ele] = index++;
                    mat[elei[ele]][in] = num; // 更新系数
                }
                ele = c;
                num = 0;
                complete = false;
            } else {
                // 如果元素还没有读取完,比如说al,只读入了一个a
                ele = ele + c;
            }
        }
    }
}

void _swap(int a, int b) {
    double tmp;
    for (int i = 0; i < m; i++) {
        tmp = mat[a][i];
        mat[a][i] = mat[b][i];
        mat[b][i] = tmp;
    }
}

void _sub(int a, int b) {
    // a行减去b行的适当倍数,使得a行第一列(子矩阵)元素为0
    double magni = mat[a][b] / mat[b][b];
    for (int i = b; i < m; i++) {
        // 遍历所有列做减法
        mat[a][i] -= mat[b][i] * magni;
    }
}

int main() {
    cin >> n;
    while (n--) {
        cin >> m; //物质的个数
        string str;
        initMat();
        elei.clear();
        index = 0;

        for (int i = 0; i < m; i++) {
            cin >> str;
            getElement(str, index, i);
        }
        // 做完上述操作之后,index代表该方程式中元素的个数
        for (int i = 0; i < m; i++) {
            // 对于矩阵的每一列进行遍历
            int j;
            for (j = i; j < index; j++) // 对于每一行进行遍历
                if (fabs(mat[j][i]) >= eps) break; // 找到第一个该列不为0的行号

            if (j == index) continue; // 如果该列全为0,那么对于除去该列的子矩阵重复上述判断
            else if (j != i) {
                //如果子矩阵中第一行第一列的元素为0,则将该行和与后面的某一个第一列非0的行交换,使第一行第一列的元素非0
                _swap(i, j);    //swap i j
            }

            for (j = i + 1; j < index; j++) {
                if (fabs(mat[j][i]) >= eps) {
                    _sub(j, i); //j line sub i
                }
            }
        }
        int k;
        for (k = 0; k < index && k < m; k++)
            if (fabs(mat[k][k]) <= eps) break; //判断某一行是否全为0,只需要判断对角线上元素即可,好好想想为什么
        if (k < m) cout << "Y" << endl;
        else cout << "N" << endl;
    }
    return 0;
}

注:

易错点

  • double有精度误差,不能直接和0进行比较!!!
  • _index的计算方法!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值