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的计算方法!!!