[SDOI2014] 重建
阿新 • • 發佈:2018-03-29
輸入輸出格式 namespace 有道 現在 const can light 消息 小數
題目描述
T國有N個城市,用若幹雙向道路連接。一對城市之間至多存在一條道路。 在一次洪水之後,一些道路受損無法通行。雖然已經有人開始調查道路的損毀情況,但直到現在幾乎沒有消息傳回。 辛運的是,此前T國政府調查過每條道路的強度,現在他們希望只利用這些信息估計災情。具體地,給定每條道路在洪水後仍能通行的概率,請計算仍能通行的道路恰有N-1條,且能聯通所有城市的概率。
輸入輸出格式
輸入格式:
輸入的第一行包含整數N。 接下來N行,每行N個實數,第i+l行,列的數G[i][j]表示城市i與j之間仍有道路聯通的概率。 輸入保證G[i][j]=G[j][i],且G[i][j]=0;G[i][j]至多包含兩位小數。
輸出格式:
輸出一個任意位數的實數表示答案。 你的答案與標準答案相對誤差不超過10^(-4)即視為正確。
輸入輸出樣例
輸入樣例#1:3 0 0.5 0.5 0.5 0 0.5 0.5 0.5 0輸出樣例#1:
0.375
說明
1 < N < =50
數據保證答案非零時,答案不小於10^-4
我們來考慮一下一個 度數矩陣-鄰接矩陣 的行列式的意義,就是所有該鄰接矩陣代表的圖的所有生成樹的邊權之積的和。
而我們要求的是所有生成樹中樹邊概率之積再乘上非樹邊(1-其概率)之積 的和。於是我們便可以設base=π(1-a[i][j]) ,然後令a[i][j] = a[i][j] / (1-a[i][j]),然後我們用矩陣樹定理求一下a的行列式再乘上base就是答案了。
但是當a[i][j]==1的時候,我們要近似一下a[i][j]。
#include<bits/stdc++.h> #define ll long long #define D double const int maxn=106; using namespace std; const D eps=1e-9; D a[maxn][maxn],ans=1,base=1; bool flag=0; int n; inline int zt(D x){ if(fabs(x)<eps) return 0; return x>0?1:-1; } inline void solve(){ for(int i=1;i<n;i++) for(int j=1;j<n;j++) if(i!=j) a[i][j]=-a[i][j]; for(int i=1;i<n;i++){ int pos=i; for(int j=i+1;j<n;j++) if(a[j][i]>a[pos][i]) pos=j; if(pos!=i){ ans*=-1; for(int j=i;j<n;j++) swap(a[pos][j],a[i][j]); } if(!zt(a[i][i])) break; for(int j=i+1;j<n;j++) if(zt(a[j][i])){ D tmp=a[j][i]/a[i][i]; for(int k=i;k<n;k++) a[j][k]-=a[i][k]*tmp; } ans*=a[i][i]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%lf",&a[i][j]); if(a[i][j]==1) a[i][j]-=eps; if(i<j) base=base*(1-a[i][j]); a[i][j]=a[i][j]/(1-a[i][j]); } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) a[i][i]+=a[i][j],a[j][j]+=a[i][j]; solve(); printf("%.11lf\n",ans*base); return 0; }
[SDOI2014] 重建