顯然這個圖是個 $DAG$ ,那么就可以考慮跑 $dp$ 了
先考慮沒有梯子的情況,首先把每個位置標號,越后面的位置編號越小,終點位置編號為 $1$
那么從終點往起點 $dp$ ,枚舉當前位置搖到的數字,那么有 $f[x]=\frac{\sum_{i=1}^{6}(f[x-i]+1)}{6}$,並且 $f[1]=0$
但是這是在 $x>6$ 的情況下成立的,因為如果 $x<=6$ 那么有可能不走,特殊考慮一下
首先 $f[2]$ ,那么有 $5/6$ 的概率原地不動,$1/6$ 的概率走到終點,即 $f[2]=\frac{5(f[2]+1)}{6}+\frac{f[1]+1}{6}=(\frac{5}{6}+\frac{f[1]+1}{6}) / (1-5/6)$
然后 $f[3]$ 也差不多考慮,$f[3]=\frac{4(f[3]+1)}{6}+\frac{f[1]+1}{6}+\frac{f[2]+1}{6}=(\frac{4}{6}+\frac{f[1]+1}{6}+\frac{f[2]+1}{6}) / (1-4/6)$
發現對於 $x\in [2,6]$ ,$f[x]=\frac{(\sum_{i=1}^{x-1}f[x-i]+1)/6+(7-i)/6}{1-(7-i)/6}$
現在來考慮有梯子的情況,設位置 $x$ 有一個梯子通往位置 $y$ ,那么 $f[x]$ 轉移的時候還要考慮瞬移到 $y$ 再走的情況
差不多的轉移,取個最小值即可,即和 $\sum_{i=1}^{6}(f[y-i]+1)/6$ 取個最小值,對於 $y<=6$ 的情況同樣要特殊處理
看代碼就知道具體怎么做了
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef double db; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int n=10,N=107; int id[N][N],to[N]; db f[N]; int main() { int tot=0; for(int i=1;i<=n;i++) { if(i&1) for(int j=1;j<=n;j++) id[i][j]=++tot; else for(int j=n;j>=1;j--) id[i][j]=++tot; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int a=read(); if(a) to[id[i][j]]=id[i-a][j]; } // f[2]=(f[2]+1)*5/6 + 1/6 // f[3]=(f[3]+1)*4/6 + 1/6 + (f[2]+1)/6 for(int i=2;i<=6;i++) { for(int j=1;j<i;j++) f[i]+=(f[i-j]+1.0)/6; f[i]+=(7.0-i)/6; f[i]/=(1.0-(7.0-i)/6); } for(int i=7;i<=tot;i++) { for(int j=1;j<=6;j++) f[i]+=(f[i-j]+1.0)/6; if(!to[i]) continue; db mi=0; int t=to[i]; if(t==1) { f[i]=0; continue; }//我這個做法要特判 for(int j=1;j<=6;j++) if(t-j>0) mi+=(f[t-j]+1.0)/6; if(t<=6) mi+=(7.0-t)/6,mi/=(1.0-(7.0-t)/6); f[i]=min(f[i],mi); } printf("%.10f\n",f[tot]); return 0; }