題目
分析
問題要求 由於矩陣樹只能處理 ,所以把式子變成 於是用矩陣樹定理推廣計算 ,最後乘上 即爲答案。
注意 會除零而出問題,給 減個 即可。
矩陣樹定理推廣:計算所有生成樹邊權乘積的和,即 其中 爲一個有理數。
令 ,其中 ,那麼 當邊權 爲整數的時候很好計算:把 之間連上 條邊,然後計算生成樹的個數就是原圖所有生成樹邊權乘積和。
但是 很顯然精度不夠,所以我們用 行列式的可乘性 把 分配給行列式中每個元素,於是我們有了一個新的基爾霍夫矩陣:度爲連出去所有邊的邊權和,邊權爲原邊權的相反數。然後正常矩陣樹定理得到的答案就是所求的了。
代碼
#include <bits/stdc++.h>
const int MAXN = 50;
const double eps = 1e-5;
int N;
double Mat[MAXN + 5][MAXN + 5];
void AddEdge(int u, int v, double w) {
Mat[u][u] += w, Mat[v][v] += w;
Mat[u][v] -= w, Mat[v][u] -= w;
}
inline double Abs(const double &x) {
return x < 0 ? -x : x;
}
inline int Comp(const double &x, const double &y) {
if (Abs(x - y) < eps)
return 0;
return x > y ? 1 : -1;
}
double Det(int n) {
n--;
double ret = 1;
for (int i = 1; i <= n; i++) {
int p = i;
for (int j = i; j <= n; j++)
if (Comp(Mat[p][i], Mat[j][i]) < 0)
p = j;
if (p != i)
std::swap(Mat[i], Mat[p]);
if (!Comp(Mat[i][i], 0))
return 0;
ret *= Mat[i][i];
for (int j = i + 1; j <= n; j++) {
double tmp = Mat[j][i] / Mat[i][i];
for (int k = i; k <= n; k++)
Mat[j][k] -= Mat[i][k] * tmp;
}
}
return Abs(ret);
}
int main() {
scanf("%d", &N);
double All = 1.0;
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++) {
double p; scanf("%lf", &p);
if (i < j) {
if (Comp(p, 1) == 0)
p -= eps;
AddEdge(i, j, p / (1 - p));
All *= (1 - p);
}
}
printf("%.5f", Det(N) * All);
}