bzoj1415 [Noi2005]聪聪和可可【概率dp 数学期望】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1415

noip2016 D1T3,多么痛的领悟。。。看来要恶补一下与期望相关的东西了。

这是一道经典的求期望的题,尽管我的代码里把那个记忆化搜索那个叫做dp,但事实上这不是动态规划,只是递推。

先预处理出x[i][j],表示聪聪在i,可可在j时,下一步聪聪到达的顶点标号,f[i][j]是那张记忆化搜索的表,表示聪聪在i,可可在j时,期望所需的时间,out[i]表示i点的出度(其实就是度啦,无向图没什么入度出度的)。显然,下一个单位时间聪聪的位置是x[x[i][j]][j],而可可是在所有与j相邻的节点或者j自己中随机挑一个到达,每个的概率都是1 / (out[j] + 1),所以有

f[i][j] = ( sigma(  f[x[x[i][j]][j]][k]  ) + f[x[x[i][j]][j]][j] ) / (out[j] + 1) + 1,其中k表示每个与j相邻的节点。

然后写一个记忆化搜索就完事咯~

#include <cstdio>
#include <cstring>

const int maxn = 1005, maxe = 1005;

int n, e, ini_neko, ini_mouse, t1, t2;
int head[maxn], to[maxe << 1], next[maxe << 1], lb, out[maxn];
int que[maxn], head_, tail, h, x[maxn][maxn], d[maxn][maxn];
double f[maxn][maxn];

inline void ist(int aa, int ss) {
	to[lb] = ss;
	next[lb] = head[aa];
	head[aa] = lb;
	++out[aa];
	++lb;
}
double dp(int i, int j) {
	if (f[i][j] != -1.0) {
		return f[i][j];
	}
	if (i == j) {
		return f[i][j] = 0.0;
	}
	if (x[i][j] == j) {
		return f[i][j] = 1.0;
	}
	if (x[x[i][j]][j] == j) {
		return f[i][j] = 1.0;
	}
	f[i][j] = 0.0;
	for (int k = head[j]; k != -1; k = next[k]) {
		f[i][j] += dp(x[x[i][j]][j], to[k]);
	}
	f[i][j] = (f[i][j] + dp(x[x[i][j]][j], j)) / (double)(out[j] + 1) + 1;
	return f[i][j];
}

int main(void) {
	//freopen("in.txt", "r", stdin);
	memset(head, -1, sizeof head);
	memset(next, -1, sizeof next);
	for (int i = 0; i < maxn; ++i) {
		for (int j = 0; j < maxn; ++j) {
			f[i][j] = -1.0;
		}
	}
	scanf("%d%d", &n, &e);
	scanf("%d%d", &ini_neko, &ini_mouse);
	while (e--) {
		scanf("%d%d", &t1, &t2);
		ist(t1, t2);
		ist(t2, t1);
	}
	
	memset(d, -1, sizeof d);
	for (int i = 1; i <= n; ++i) {
		memset(que, 0, sizeof que);
		head_ = tail = 0;
		que[tail++] = i;
		d[i][i] = 0;
		while (head_ != tail) {
			h = que[head_++];
			for (int j = head[h]; j != -1; j = next[j]) {
				if (d[i][to[j]] == -1) {
					d[i][to[j]] = d[i][h] + 1;
					que[tail++] = to[j];
				}
			}
		}
	}
	
	memset(x, 0x3c, sizeof x);
	for (int i = 1; i <= n; ++i) {
		for (int k = head[i]; k != -1; k = next[k]) {
			for (int j = 1; j <= n; ++j) {
				if (d[i][j] == d[to[k]][j] + 1 && x[i][j] > to[k]) {
					x[i][j] = to[k];
				}
			}
		}
	}
	
	printf("%.3f\n", dp(ini_neko, ini_mouse));
	return 0;
}

  

posted @ 2016-11-28 20:39  ciao_sora  阅读(382)  评论(0编辑  收藏  举报