E42 概率DP 求期望 高斯消元
视频链接:https://www.bilibili.com/video/BV1kP411m7xv/
时间:O(n^3+mlogm)
#include<cmath> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=510,M=125010; const double eps=1e-6; int n,m; int h[N],to[M<<1],ne[M<<1],tot; int d[N],s[M],t[M]; //点的度,边的起点、终点 double a[N][N],g[M],ans; void add(int u,int v){ to[++tot]=v;ne[tot]=h[u];h[u]=tot; } void gauss(){ for(int i=1; i<n; ++i){ //第i主元 for(int k=i; k<n; ++k) //换非0行 if(fabs(a[k][i])>eps) {swap(a[k],a[i]); break;} for(int k=1; k<n; ++k) //对角化 if(k!=i) for(int j=n; j>=i; --j) a[k][j]-=a[k][i]/a[i][i]*a[i][j]; } for(int i=1;i<n;++i) a[i][n]/=a[i][i]; //除以主元 } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&s[i],&t[i]); add(s[i],t[i]); add(t[i],s[i]); d[s[i]]++; d[t[i]]++; } for(int u=1;u<n;u++){ //构造增广矩阵 for(int i=h[u];i;i=ne[i]){ int v=to[i]; if(v!=n)a[u][v]=-1.0/d[v];//从v走到u的概率 } a[u][u]=1; //fu移项后的系数 } a[1][n]=1; //f1的常数项 gauss(); //点的期望次数 for(int i=1;i<=m;i++) //边的期望次数 g[i]=a[s[i]][n]/d[s[i]]+a[t[i]][n]/d[t[i]]; sort(g+1,g+m+1); for(int i=1;i<=m;i++) ans+=g[i]*(m-i+1); //次数多,编号小(贪心) printf("%.3lf\n",ans); }
练习:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N=1010; double a[N][N],f[N]; int n,m,st,ed; void solve(){ for(int k=n-1; k>=st; k--){ memset(a,0,sizeof a); for(int i=1; i<=m; i++){ if(i==1){ a[i][i]=2; a[i][i+1]=-1; a[i][m+1]=3+f[i]; continue; } else if(i==m){ a[i][i]=2; a[i][i-1]=-1; a[i][m+1]=3+f[i]; continue; } a[i][i]=3; a[i][i+1]=-1; a[i][i-1]=-1; a[i][m+1]=4+f[i]; } for(int i=1; i<m; i++){ double p=a[i+1][i]/a[i][i]; a[i+1][i]=0; a[i+1][i+1]-=a[i][i+1]*p; a[i+1][m+1]-=a[i][m+1]*p; } f[m]=a[m][m+1]/a[m][m]; for(int i=m-1; i>=1; i--) f[i]=(a[i][m+1]-f[i+1]*a[i][i+1])/a[i][i]; } } int main(){ scanf("%d %d",&n,&m); scanf("%d %d",&st,&ed); if(m==1){ printf("%.10f\n",2.0*(n-st)); return 0; } solve(); printf("%.10f\n",f[ed]); }