【BZOJ3640】JC的小蘋果 概率DP+高斯消元
【BZOJ3640】JC的小蘋果
Description
讓我們繼續JC和DZY的故事。
“你是我的小丫小蘋果,怎麽愛你都不嫌多!”
“點亮我生命的火,火火火火火!”
話說JC歷經艱辛來到了城市B,但是由於他的疏忽DZY偷走了他的小蘋果!沒有小蘋果怎麽聽歌!他發現邪惡的DZY把他的小蘋果藏在了一個迷宮裏。JC在經歷了之前的戰鬥後他還剩下hp點血。開始JC在1號點,他的小蘋果在N號點。DZY在一些點裏放了怪獸。當JC每次遇到位置在i的怪獸時他會損失Ai點血。當JC的血小於等於0時他就會被自動彈出迷宮並且再也無法進入。
但是JC迷路了,他每次只能從當前所在點出發等概率的選擇一條道路走。所有道路都是雙向的,一共有m條,怪獸無法被殺死。現在JC想知道他找到他的小蘋果的概率。
P.S.大家都知道這個系列是提高組模擬賽,所以這是一道送分題balabala
Input
第一行三個整數表示n,m,hp。接下來一行整數,第i個表示jc到第i個點要損失的血量。保證第1個和n個數為0。接下來m行每行兩個整數a,b表示ab間有一條無向邊。
Output
僅一行,表示JC找到他的小蘋果的期望概率,保留八位小數。
Sample Input
3 3 20 1 0
1 2
2 3
Sample Output
0.87500000HINT
對於100%的數據 2<=n<=150,hp<=10000,m<=5000,保證圖聯通。
題解:如果沒有Ai=0,直接DP,如果hp很小,直接高斯消元,但是都有,所以用DP+高斯消元。
發現,方程組中只有Ai=0的點有系數,Ai!=0的都可以直接拿過來變成常數項,所以每次我們的方程組只有一列是變化的,所以我們將方程組表示成Ax=b,x=A-1b。x和b都是列向量。所以我們只需要預處理出A的逆,然後每次O(n2)乘一下就行了。
矩陣求逆的方法:先將(A|I)拼一起,然後通過行變換將左邊的A消成I,右邊剩下的就是A-1
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> using namespace std; int n,m,hp; int dam[160],pa[5010],pb[5010],d[160]; double f[160][10000],B[160],ans; struct M { double v[160][160]; M (){memset(v,0,sizeof(v));} double* operator [](int x) {return v[x];} void I() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) v[i][j]=(i==j)?1:0; } M getinv() { int i,j,k; M re; double t; re.I(); for(i=1;i<=n;i++) { for(j=i;j<=n;j++) if(fabs(v[j][i])>fabs(v[i][i])) for(k=1;k<=n;k++) swap(v[i][k],v[j][k]),swap(re[i][k],re[j][k]); t=v[i][i]; for(j=1;j<=n;j++) v[i][j]/=t,re[i][j]/=t; for(j=1;j<=n;j++) if(i!=j) { t=v[j][i]; for(k=1;k<=n;k++) v[j][k]-=t*v[i][k],re[j][k]-=t*re[i][k]; } } return re; } void operator * (int x) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][x]+=v[i][j]*B[j]; } }; M A,A1; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } int main() { n=rd(),m=rd(),hp=rd(); int i,j; for(i=1;i<=n;i++) dam[i]=rd(); for(i=1;i<=m;i++) { pa[i]=rd(),pb[i]=rd(); d[pa[i]]++; if(pa[i]!=pb[i]) d[pb[i]]++; } for(i=1;i<=m;i++) { if(!dam[pb[i]]) A[pb[i]][pa[i]]-=1.0/d[pa[i]]; if(pa[i]==pb[i]) continue; if(!dam[pa[i]]) A[pa[i]][pb[i]]-=1.0/d[pb[i]]; } for(i=1;i<=n;i++) A[i][n]=0; for(i=1;i<=n;i++) A[i][i]++; A1=A.getinv(); for(j=hp;j;j--) { for(i=1;i<=n;i++) B[i]=0; if(j==hp) B[1]=1; else for(i=1;i<=m;i++) { if(dam[pb[i]]&&dam[pb[i]]+j<=hp&&pa[i]!=n) B[pb[i]]+=f[pa[i]][j+dam[pb[i]]]*1.0/d[pa[i]]; if(pa[i]==pb[i]) continue; if(dam[pa[i]]&&dam[pa[i]]+j<=hp&&pb[i]!=n) B[pa[i]]+=f[pb[i]][j+dam[pa[i]]]*1.0/d[pb[i]]; } A1*j,ans+=f[n][j]; } printf("%.8lf",ans); return 0; }
【BZOJ3640】JC的小蘋果 概率DP+高斯消元