[蓝桥杯 2022 国 B] 故障
题目描述
在软件或系统开发中,我们会遇到各种各样的故障。为了从故障现象反推故障原因,工程师们会总结一种叫做相关性矩阵的二维表格,来表示故障原因与故障现象之间的关系。比如:
其中每行表示一种故障原因,每一列表示一种故障现象。该矩阵表示故障原因 A A A 可能产生故障现象 2 2 2、 3 3 3、 4 4 4,故障原因 B B B 可能产生故障现象 1 1 1、 3 3 3。
在实际开发过程中,如果出现了故障原因,工程师就可以根据故障现象,去计算每种故障原因产生的概率,并按照概率大小对故障原因进行排查,以达到快速定位故障原因的目的。
现在,我们假设系统开发中同一时间只会出现一种故障原因, 并且故障原 因引起各故障现象是独立事件。举个例子来说:
假设系统现在发生了故障原因 A A A, 有 1 3 \frac{1}{3} 31 的概率出现故障现象 2 2 2,有 1 4 \frac{1}{4} 41 的概率出现故障现象 3 3 3,有 1 2 \frac{1}{2} 21 的概率出现故障现象 4 4 4。由于 3 3 3 种现象是独立发生的,因此有 1 2 × 3 × 4 \frac{1}{2 \times 3 \times 4} 2×3×41 的概率同时出现故障 2 2 2、 3 3 3、 4 4 4。
约定若相关性矩阵中没有 x
记号, 则表示该故障原因一定不会产生某故障现象,比如故障原因
A
A
A,一定不会产生故障现象
1
1
1。根据历史经验数据,我们统计得到了每一种故障原因出现的概率以及每一种故障原因对应的故障现象产生概率。
现在已知系统出现的故障现象,求问各个故障原因发生的概率。
输入格式
第 1 1 1 行: 2 2 2 个正整数 N , M , N N, M, N N,M,N 表示故障原因的个数(编号 1 … N ) , M 1 \ldots N),M 1…N),M 表示故障现象的个数(编号 1 … M 1 \ldots M 1…M)。
第 2 2 2 行: N N N 个整数,第 i i i 个数表示故障原因 i i i 产生的概率 P i P_{i} Pi。
第 3 … N + 2 3 \ldots N+2 3…N+2 行:每行 M M M 个整数,第 i + 2 i+2 i+2 行第 j j j 个整数 P i j P_{i j} Pij 表示故障原因 i i i 出现故障现象 j j j 的概率(百分比)。
第 N + 3 N+3 N+3 行: 1 1 1 个正整数 K K K,表示目前出现的故障现象数量。
第 N + 4 N+4 N+4 行: K K K 个正整数,依次为当前出现的故障现象编号,不会重复。
输出格式
第 1 … N 1 \ldots N 1…N 行:按概率从高到低输出每种故障原因及其可能的概率,若出现 概率相同则优先输出编号小的故障原因。第 1 1 1 个数为故障原因编号, 第 2 2 2 个数为故障概率(百分比),保留 2 2 2 位小数。
样例 #1
样例输入 #1
3 5
30 20 50
0 50 33 25 0
30 0 35 0 0
0 0 0 25 60
1
3
样例输出 #1
2 56.89
1 43.11
3 0.00
提示
对于所有测试用例, 1 ≤ N ≤ 40 , 1 ≤ M ≤ 20 , 0 ≤ P i ≤ 100 , ∑ ( P i ) = 100 1 \leq N \leq 40,1 \leq M \leq 20,0 \leq P_{i} \leq 100, \sum\left(P_{i}\right)=100 1≤N≤40,1≤M≤20,0≤Pi≤100,∑(Pi)=100, 0 ≤ P i j ≤ 100 0 \leq P_{i j} \leq 100 0≤Pij≤100。
蓝桥杯 2022 国赛 B 组 G 题。
思路
贝叶斯公式是一种关于随机事件 A A A 和 B B B 的条件概率的定理,其一般形式为:
P ( A ∣ B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B) = \frac{P(B|A)P(A)}{P(B)} P(A∣B)=P(B)P(B∣A)P(A)
在这个问题中, A A A 表示故障原因, B B B 表示故障现象。 P ( A ∣ B ) P(A|B) P(A∣B) 就是在已知故障现象的情况下,故障原因 A A A 发生的概率,也就是我们需要求解的。 P ( B ∣ A ) P(B|A) P(B∣A) 是在已知故障原因的情况下,故障现象 B B B 发生的概率,这个概率在输入中给出。 P ( A ) P(A) P(A) 是故障原因 A A A 发生的概率,这个概率也在输入中给出。 P ( B ) P(B) P(B) 是故障现象 B B B 发生的概率,可以通过遍历所有的故障原因并将 P ( B ∣ A ) P ( A ) P(B|A)P(A) P(B∣A)P(A) 相加得到。
在计算 P ( A ∣ B ) P(A|B) P(A∣B) 的过程中,首先对于每个故障原因 i i i,计算其出现的概率 a a a,这个概率是由该故障原因出现的概率 p i [ i ] pi[i] pi[i] 和其引起所有出现的故障现象的概率的乘积得到的。如果故障现象 j j j 出现,那么就乘以 p i j [ i ] [ j ] pij[i][j] pij[i][j],否则乘以 1 − p i j [ i ] [ j ] 1 - pij[i][j] 1−pij[i][j]。这就是计算 P ( B ∣ A ) P ( A ) P(B|A)P(A) P(B∣A)P(A) 的过程。然后将所有的 P ( B ∣ A ) P ( A ) P(B|A)P(A) P(B∣A)P(A) 相加得到 P ( B ) P(B) P(B),也就是变量 b b b。
最后,对于每个故障原因 i i i,其出现的概率 P ( A ∣ B ) P(A|B) P(A∣B) 就等于 a / b a/b a/b,即 P ( B ∣ A ) P ( A ) / P ( B ) P(B|A)P(A)/P(B) P(B∣A)P(A)/P(B)。
样例的相关性矩阵可以表示为:
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
A | 0 | 50 | 33 | 25 | 0 |
B | 30 | 0 | 35 | 0 | 0 |
C | 0 | 0 | 0 | 25 | 60 |
首先通过scanf
函数从输入中读取故障原因的数量n
和故障现象的数量m
。然后,读取每个故障原因出现的概率pi[i]
,并将其转换为小数形式。接着,读取故障原因i
出现故障现象j
的概率pij[i][j]
,同样将其转换为小数形式。
读取现在出现的故障现象数量k
,然后读取这k
个出现的故障现象的编号,并用位集err
记录下来。
定义一个变量b
来存储所有故障原因的概率总和,然后遍历所有的故障原因,对于每个故障原因i
,计算其出现的概率a
。这个概率a
是由该故障原因出现的概率pi[i]
和其引起所有出现的故障现象的概率的乘积得到的。如果故障现象j
出现,那么就乘以pij[i][j]
,否则乘以1 - pij[i][j]
。然后将a
加到b
中,并将故障原因的编号和概率a
存入ans
向量中。
最后,对ans
向量进行排序,然后按照题目要求的格式输出每个故障原因及其可能的概率。
AC代码
#include <algorithm>
#include <bitset>
#include <cmath>
#include <iostream>
#include <vector>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;
const int N = 1e3 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
struct S {
int id;
double p;
bool operator<(const S &b) const {
if (p == b.p) {
return id < b.id;
}
return p > b.p;
}
};
int n, m, k;
double pi[N];
double pij[N][N];
vector<S> ans;
bitset<N> err;
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lf", &(pi[i]));
pi[i] *= 0.01;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
scanf("%lf", &(pij[i][j]));
pij[i][j] *= 0.01;
}
}
scanf("%d", &k);
err.reset();
for (int i = 1; i <= k; i++) {
int e;
scanf("%d", &e);
err[e] = 1;
}
// cout << err.count() << endl;
double b = 0;
for (int i = 1; i <= n; i++) {
double a = 1;
for (int j = 1; j <= m; j++) {
if (err[j]) {
// 存在现象
a *= pij[i][j];
} else {
// 现象不存在
a *= 1 - pij[i][j];
}
}
a *= pi[i];
b += a;
ans.push_back({i, a});
// cout << i << " " << a << endl;
}
// cout << b << endl;
sort(ans.begin(), ans.end());
for (const auto i : ans) {
printf("%d %.2lf\n", i.id, i.p / b * 100);
}
return 0;
}