# PAT_甲级_2020年冬季考试 7-4 Chemical Equation

## 7-4 Chemical Equation (30 分)

A chemical equation is the symbolic representation of a chemical reaction in the form of symbols and formulae, wherein the reactant entities are given on the left-hand side and the product entities on the right-hand side. For example, \$\$CH_4+2O_2=CO_2+2H_2O\$\$

means that the reactants in this chemical reaction are methane and oxygen: CH4 and O​2, and the products of this reaction are carbon dioxide and water: CO2 and H2O.

Given a set of reactants and products, you are supposed to tell that in which way we can obtain these products, provided that each reactant can be used only once. For the sake of simplicity, we will consider all the entities on the right-hand side of the equation as one single product.

### Input Specification:

Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤20), followed by N distinct indices of reactants. The second line gives an integer M (1≤M≤10), followed by M distinct indices of products. The index of an entity is a 2-digit number.

Then a positive integer K (≤50) is given, followed by K lines of equations, in the format:

\$\$ reactant_1 + reactant_2 + ... + reactant_n -> product \$\$

where all the reactants are distinct and are in increasing order of their indices.

#### Note: It is guaranteed that

• one set of reactants will not produce two or more different products, i.e. situation like 01 + 02 -> 03 and 01 + 02 -> 04 is impossible;
• a reactant cannot be its product unless it is the only one on the left-hand side, i.e. 01 -> 01 is always true (no matter the equation is given or not), but 01 + 02 -> 01 is impossible; and
• there are never more than 5 different ways of obtaining a product given in the equations list.

### Output Specification:

For each case, print the equations that use the given reactants to obtain all the given products. Note that each reactant can be used only once.

Each equation occupies a line, in the same format as we see in the inputs. The equations must be print in the same order as the products given in the input. For each product in order, if the solution is not unique, always print the one with the smallest sequence of reactants -- A sequence \$\${ a_1,⋯,a_​m }\$\$ is said to be smaller than another sequence \$\${ b_1 ,⋯,b​_n } \$\$if there exists \$\$1≤i≤min(m,n)\$\$ so that \$\$a_j​​ =b_j \;\; for \;\;all\;\; j<i, and\;\; a_i​​ <b_i.\$\$

It is guaranteed that at least one solution exists.

### Sample Input:

``````8 09 05 03 04 02 01 16 10
3 08 03 04
6
03 + 09 -> 08
02 + 08 -> 04
02 + 04 -> 03
01 + 05 -> 03
01 + 09 + 16 -> 03
02 + 03 + 05 -> 08``````

### Sample Output:

``````02 + 03 + 05 -> 08
01 + 09 + 16 -> 03
04 -> 04``````

### 题目大意

• 1、一组反应物只会生成一种product
• 2、除了 pro->pro 的情况，一个化学方程式是不可能出现反应物中存在生成物的情况
• 3、对于有多个生成的化学式输出字典序最小的那个。
• 4、每一种反应物只能使用一次

### 算法思路

• 数据结构：
``````struct Equation {
string equation;
vector<int> reacts;
int prod{};

friend bool operator<(const Equation &a, const Equation &b) {
return a.equation < b.equation;
}
};
unordered_map<int, bool> reactants;// 标记当前反应物是否可以使用
unordered_map<int, set<Equation>> products;// 根据product建立到所有生成该生成物的Equation集合

vector<int> pro;// 所有需要生成的生成物编号
vector<string> ans;// 所有的结果集合``````

#### DFS

• 递归边界：当我们当前遍历的下标为m的时候说明遍历完毕，获得了一组解，直接返回true。
• 递归体：首先遍历当前生成物pro[proCur]的所有化学方程式，判断当前的方程是是否可以使用，如果可以，先标记当前化学方程式的生成物全部已经使用，并将方程式添加到ans数组中，然后进行下一层递归，判断proCur + 1以及后续的生成物是否可以被生成，如果为true，说明从proCur位置到数组末尾的所有生成物都可以被生成，返回true。否则说明后续生成物生成失败，需要回溯，先将所有的反应物标记为未使用，然后ans弹出之前添加的方程式。

### 注意点

• 1、这里使用DFS主要是因为需要生成所有的生成物，一个都不能落下，所以贪心算法不可用，测试点1和5考察该点。
• 2、每一个查询的生成物都可以生成 product->product的化学方程式，需要人为添加该式子到集合中。

### AC代码

``````#include<cstdio>
#include<vector>
#include<iostream>
#include<unordered_map>
#include <set>
#include <string>

using namespace std;

struct Equation {
string equation;
vector<int> reacts;
int prod{};

friend bool operator<(const Equation &a, const Equation &b) {
return a.equation < b.equation;
}
};

unordered_map<int, bool> reactants;// 标记当前反应物是否可以使用
unordered_map<int, set<Equation>> products;// 根据product建立到所有生成该生成物的Equation集合

vector<int> pro;// 所有需要生成的生成物编号
vector<string> ans;// 所有的结果集合

// 将s封装为一个Equation
void process(const string &s) {
int n = s.size();
if (n == 0) {
return;
}
Equation e;
// 最后两位为生成物编号
e.prod = stoi(s.substr(n - 2));
// 分解字符串获取反应物
string temp;
for (int i = 0; i < n; ++i) {
if (s[i] >= '0' && s[i] <= '9') {
temp.push_back(s[i]);
} else if (s[i] == ' ' && !temp.empty()) {
int v = stoi(temp);
e.reacts.push_back(v);
temp.clear();
} else if (s[i] == '-') {
break;
}
}
e.equation = s;
products[e.prod].insert(e);
}

// 判断当前的生成物pro[proCur]是否可以被生成，如果不可以就得回溯
bool DFS(int proCur, int proEnd) {
if (proCur == proEnd) {
// 此时已经将所有的生成物生成完毕
return true;
}
// 生成物编号
int proNo = pro[proCur];
// 遍历当前生成物proNo的所有化学方程式
for (const auto &allEquations:products[proNo]) {
// 判断当前的方程是是否可以使用（所有的反应物是否都没有被使用过）
bool isvalid = true;
for (int react:allEquations.reacts) {
if (!reactants[react]) {
// 当前反应物被使用过
isvalid = false;
break;
}
}
if (!isvalid) {
continue;
}
// 存在一种解
ans.push_back(allEquations.equation);
// 然后将所有的反应物都进行标记使用过
for (int react:allEquations.reacts) {
reactants[react] = false;
}
// 递归查询后续的生成物是否可以生成
if (DFS(proCur + 1, proEnd)) {
// 可以生成说明找到了一组解，直接返回即可
return true;
}
// 当前选择的化学方程式使得后续的生成物无法生成，需要回溯
for (int react:allEquations.reacts) {
reactants[react] = true;
}
ans.pop_back();
}
// 遍历了所有的化学生成式都没有办法获得当前生成物
return false;
}

int main() {
// n为反应物数量，m为生成物数量
int n, m;
scanf("%d", &n);
int index;
for (int i = 0; i < n; ++i) {
scanf("%d", &index);
// 标记所有给出的反应式
reactants[index] = true;
}
scanf("%d", &m);
for (int i = 0; i < m; ++i) {
string s;
cin >> s;
pro.push_back(stoi(s));
// 每一个反应物都存在一个 pro->pro 的化学方程式
Equation e;
e.equation.append(s);
e.equation.append(" -> ");
e.equation.append(s);
e.prod = pro[i];
e.reacts.push_back(pro[i]);
// 标记当前的生成物同时为反应物
reactants[pro[i]] = true;
// 将当前的pro->pro的化学方程式添加到products集合中
products[pro[i]].insert(e);
}
int k;
scanf("%d", &k);
// 吸收回车
getchar();
string s;
for (int i = 0; i < k; ++i) {
getline(cin, s);
// 处理每一个化学方程式，封装为Equation
process(s);
}
// 深度搜索，保证获得所有的生成物
DFS(0, m);
// 输出结果集合
for (const auto &v:ans) {
cout << v << endl;
}
return 0;
}``````

566 声望
15 粉丝
0 条评论