[NOI2005]聪聪与可可

题目链接:Click here

Solution:

首先设(f(i,j))表示聪聪在点(i),可可在点(j)时的期望时间,设(tot_x)表示与(x)相邻的点的个数,(to_i)表示与(i)相邻的点集

因为可可每次可能走到与自己相邻的某个点,也可能不动,这些选择都是等概率的,则我们可以得出式子:

[f(i,j)=frac{1}{tot_j+1}(1+f(now,j)+sum_{vin to_j}1+f(now,v))\ f(i,j)=frac{1}{tot_j+1}(f(now,j)+sum_{vin to_j}f(now,v))+1 ]

dp的过程我们可以用记忆化搜索来完成,现在我们只需要知道对于每一个(f(i,j))(now)是多少就行了

这个也很简单,先用spfa处理出距离,再来预处理就好了,最后我们只需要输出(f(S,T))就行了

Code:

#include<bits/stdc++.h>
const int N=1e3+1;
const int inf=2147483647;
using namespace std;
int n,m,s,t,cnt,head[N],dis[N][N];
int go[N][N],apr[N][N],vis[N];
double f[N][N];
struct Edge{int nxt,to;}edge[N<<1];
void ins(int x,int y){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;head[x]=cnt;
}
queue<int> q;
void spfa(int v0){
	for(int i=1;i<=n;i++)
		dis[v0][i]=inf;
	dis[v0][v0]=0;q.push(v0);
	memset(vis,0,sizeof(vis));
	while(!q.empty()){
		int x=q.front();q.pop();vis[x]=0;
		for(int i=head[x];i;i=edge[i].nxt){
			int y=edge[i].to;
			if(dis[v0][x]+1<dis[v0][y]){
				dis[v0][y]=dis[v0][x]+1;
				if(!vis[y]) q.push(y),vis[y]=1;
			}
		}
	}
}
double dfs(int x,int y){
	if(apr[x][y]) return f[x][y];
	apr[x][y]=1;int tot=1;
	int u1=go[x][y],u2=go[u1][y];
	if(u1==y||u2==y) return f[x][y]=1.0;
	f[x][y]=1.0;
	for(int i=head[y];i;i=edge[i].nxt) ++tot;
	for(int i=head[y];i;i=edge[i].nxt){
		int to=edge[i].to;
		f[x][y]+=1.0*1/tot*dfs(u2,to);
	}f[x][y]+=1.0/tot*dfs(u2,y);
	return f[x][y];
}
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main(){
	n=read(),m=read(),s=read(),t=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		ins(x,y),ins(y,x);
	}
	for(int i=1;i<=n;i++) spfa(i);
	for(int i=1;i<=n;i++) apr[i][i]=1,f[i][i]=0.0;
	memset(go,0x3f,sizeof(go));
	for(int i=1;i<=n;i++)	
		for(int u=head[i];u;u=edge[u].nxt){
			int y=edge[u].to;
			for(int j=1;j<=n;j++)
				if(dis[y][j]+1==dis[i][j])
					go[i][j]=min(go[i][j],y);
		}printf("%.3lf
",dfs(s,t));
	return 0;
}
原文地址:https://www.cnblogs.com/NLDQY/p/11170426.html