NOI2005 聪聪与可可 期望DP+记忆化搜索

传送门

一道期望DP。
d p [ i ] [ j ] dp[i][j] dp[i][j]为i到j的期望距离,则有
d p [ i ] [ j ] = ( d p [ s [ s [ i ] [ j ] ] [ j ] ] [ v ] + 1 ) / ( s i z e [ j ] + 1 ) + 1 dp[i][j] = (dp[s[s[i][j]][j]][v]+1)/(size[j]+1)+1 dp[i][j]=(dp[s[s[i][j]][j]][v]+1)/(size[j]+1)+1,其中size表示该点的出度, s [ i ] [ j ] s[i][j] s[i][j]表示从i到j下一步走到哪,v表示i的所有一步可到达的点,对了还要包括j点本身。
最后记忆化搜索即可,注意由于每次是老鼠先走,状态转移方程应为 d p [ u ] [ e n d ] + = ( d f s ( s [ s [ u ] [ e n d ] ] [ e n d ] , v ) + 1 ) / ( d o u b l e ) ( s i z e [ e n d ] + 1 ) dp[u][end] += (dfs(s[s[u][end]][end], v) + 1) / (double)(size[end] + 1) dp[u][end]+=(dfs(s[s[u][end]][end],v)+1)/(double)(size[end]+1)
而不是
d p [ u ] [ e n d ] + = ( d f s ( s [ s [ u ] [ v ] ] [ v ] , v ) + 1 ) / ( d o u b l e ) ( s i z e [ e n d ] + 1 ) dp[u][end] += (dfs(s[s[u][v]][v], v) + 1) / (double)(size[end] + 1) dp[u][end]+=(dfs(s[s[u][v]][v],v)+1)/(double)(size[end]+1)

#include
#include
#include
#include
using namespace std;
const int MAXN = 1001;
const int MAXM = 1001;

int fir[MAXN], nxt[MAXM << 1], to[MAXM << 1], cnt;
int size[MAXN], dis[MAXN][MAXN], s[MAXN][MAXN], vis[MAXN];
double dp[MAXN][MAXN];

inline int read(){
	int k = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
	return k * f;
}

inline void add_edge(int a, int b){
	to[cnt] = b;
	nxt[cnt] = fir[a];
	fir[a] = cnt++;
}

void bfs(int begin){
	queue <int> q; q.push(begin);
	dis[begin][begin] = 0;
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(int i = fir[u]; i != -1; i = nxt[i]){
			int v = to[i];
			if(dis[begin][v] == -1){
				dis[begin][v] = dis[begin][u] + 1;
				q.push(v);
			}
		}
	}
}

double dfs(int u, int end){
	if(dp[u][end] == -1){
		if(u == end){
			dp[u][end] = 0;
		}
		else if(s[u][end] == end){
			dp[u][end] = 1;
		}
		else if(s[s[u][end]][end] == end){
			dp[u][end] = 1;
		}
		else{
			dp[u][end] = 0; //先初始化为0 
			for(int i = fir[end]; i != -1; i = nxt[i]){
				int v = to[i];
				dp[u][end] += (dfs(s[s[u][end]][end], v) + 1) / (double)(size[end] + 1);
			}
			dp[u][end] += (dfs(s[s[u][end]][end], end) + 1) / (double)(size[end] + 1);
		}
	}
	return dp[u][end];
}

int main(){
	freopen("in.txt", "r", stdin);
	memset(fir, -1, sizeof(fir));
	memset(dis, -1, sizeof(dis));
	memset(s, 0x3f, sizeof(s));
	int n = read(), m = read();
	int C1 = read(), C2 = read();
	for(int i = 1; i <= m; i++){
		int a = read(), b = read();
		add_edge(a, b);
		add_edge(b, a);
		size[a]++, size[b]++;
	}
	
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			dp[i][j] = -1;
		}
	}
	
	for(int i = 1; i <= n; i++){
		bfs(i);
	}
	
	for(int u = 1; u <= n; u++){
		for(int i = fir[u]; i != -1; i = nxt[i]){
			int v = to[i];
			for(int j = 1; j <= n; j++){
				if(dis[u][j] == dis[v][j] + 1 && s[u][j] > v){
					s[u][j] = v;
				}
			}
		}
	}
	//预处理i到j应该走的下一步
	
	double Ans = dfs(C1, C2);
	
	printf("%.3lf", Ans);
	
	return 0;
}

你可能感兴趣的:(DP/递推,期望,记忆化搜索)