矩阵-树定理 + 概率
挂题解呀:http://blog.csdn.net/iamzky/article/details/41317333
想了解这方面就去看论文:《生成树的计数及其应用》周冬(没有链接,不用点了)
这道题告诉我们:邻接矩阵中的的权可以不是1,而是其他权值,比如概率(感性认识,猜想算行列式的时候其实就是在把矩阵中树的边权乘起来,类似于重边就直接加在边权上从而使它乘起来翻倍?不会证明)
然后我就被精度卡了很久,后来发现别的都不管用,必须要在下文代码注释的地方那样打才能过。这样会把这一项绝对值最大的提上来。
原因:我猜是用大的放上面去消小的,大的那行乘的倍数是小/大,倍数较小,乘出来的值就较小,能够保证小的被消完之后,余下的f[j][i+1],f[j][i+2]…等项能保持较小。用小的去消大的,由于用的倍数是大/小,所以余下的项可能变得较大。然后就炸精度了(吗)。应该是个好习惯。
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 52
using namespace std;
namespace runzhe2000
{
const double eps = 1e-7;
int n;
double f[N][N], pro = 1;
double Gauss()
{
double det = 1;
for(int i = 1; i < n; i++)
{
/*↓↓↓把绝对值最大的一行换上来↓↓↓*/
int r = i;
for(int j = i; j < n; j++)
if(fabs(f[j][i]) > fabs(f[r][i])) r = j;
if(r != i) for(int j = 1; j < n; j++) swap(f[i][j], f[r][j]);
/*↑↑↑要不然我也不知道为什么会被卡精度↑↑↑*/
if(-eps < f[i][i] && f[i][i] < eps) return 0;
for(int j = i+1; j < n; j++)
{
double tmp = -f[j][i]/f[i][i];
for(int k = i; k <= n; k++)
f[j][k] += tmp * f[i][k];
}
det *= f[i][i];
}
return det < 0 ? -det : det;
}
void main()
{
scanf("%d",&n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
{
scanf("%lf",&f[i][j]);
if(f[i][j] > 1-eps) f[i][j] -= eps;
if(i < j)pro *= (1-f[i][j]);
f[i][j] /= (1-f[i][j]);
}
for(int i = 1; i <= n; i++)
{
double sum = 0;
for(int j = 1; j <= n; j++) sum += f[i][j];
f[i][i] = -sum;
}
printf("%.8lf\n",Gauss() * pro);
}
}
int main()
{
runzhe2000::main();
}