Problem
Solution
首先我们可以直接bfs预处理出nxt[i][j],其表示聪聪在i节点,可可在j节点时,聪聪下一步将去往的节点,那么我们就只需要考虑可可的走的可能性。
不妨设f[i][j]表示当聪聪在i节点,可可在j节点时,期望要走的步数。首先边界情况是f[i][i]=0,直接考虑其状态转移方程,枚举各种状态。
在这里,我们用nxt表示走了两步之后到达的节点,注意是聪聪先走
f[i][j]=f[nxt][j]+∑j−>kf[nxt][k]d[j]+1+1
f
[
i
]
[
j
]
=
f
[
n
x
t
]
[
j
]
+
∑
j
−
>
k
f
[
n
x
t
]
[
k
]
d
[
j
]
+
1
+
1
我们可以很方便地用记忆化搜索解决。
Code
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=1010,INF=0x3f3f3f3f;
const double eps=1e-8;
struct data{int v,nxt;}edge[maxn<<1];
int n,m,s,t,p,d[maxn],head[maxn],dis[maxn],nxt[maxn][maxn];
double f[maxn][maxn];
queue<int> q;
inline double abs(double x){return x<0?-x:x;}
inline void insert(int u,int v)
{
edge[++p]=(data){v,head[u]};head[u]=p;
edge[++p]=(data){u,head[v]};head[v]=p;
}
void input()
{
int u,v;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
insert(u,v);d[u]++;d[v]++;
}
}
void get(int k)
{
int x;
while(!q.empty()) q.pop();
memset(dis,0x3f,sizeof(dis));
q.push(k);dis[k]=0;nxt[k][k]=k;
while(!q.empty())
{
x=q.front();q.pop();
for(int i=head[x];i;i=edge[i].nxt)
{
if(dis[edge[i].v]==INF)
{
dis[edge[i].v]=dis[x]+1;
nxt[edge[i].v][k]=x;
q.push(edge[i].v);
}
else if(dis[edge[i].v]==dis[x]+1&&nxt[edge[i].v][k]>x)
nxt[edge[i].v][k]=x;
}
}
}
double dp(int x,int y)
{
if(abs(f[x][y]+1)>eps) return f[x][y];
if(x==y) return f[x][y]=0.0;
if(nxt[x][y]==y||nxt[nxt[x][y]][y]==y) return f[x][y]=1.0;
f[x][y]=0.0;
for(int i=head[y];i;i=edge[i].nxt)
f[x][y]+=dp(nxt[nxt[x][y]][y],edge[i].v);
f[x][y]=(f[x][y]+dp(nxt[nxt[x][y]][y],y))/(double)(d[y]+1)+1.0;
return f[x][y];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();
for(int i=1;i<=n;i++) get(i);
memset(f,-1,sizeof(f));
printf("%.3lf\n",dp(s,t));
return 0;
}