http://acm.hdu.edu.cn/showproblem.php?pid=4035
是一道很好的题目。题意是有一个迷宫,这里有n个房间,每一对房间有且只有一条隧道,一共有n-1条隧道。起初他在1号房间。他若当前在房间i,接下来有三种路径可以走:ki的概率被杀掉直接回到1号房间;ei的概率从该房间逃走,否则它有均等的概率通过隧道走到和i号房间相连的房间。问它从1号房间逃出去要走的隧道数目的期望。
设dp[i]表示在i号房间走出去要通过的隧道的期望,n-1条边将房间连成一颗无根树,对于叶子节点,与它相连的只有一个父亲节点,那么dp[i] = ki * dp[1] + ei * 0 + (1-ki-ei) * ( dp[fa[i]] + 1);
对于非叶子节点
dp[i] = ki * dp[1] + ei * 0 + (1-ki-ei)/m * (∑dp[ son[i] ] + dp[ fa[i] ] + 1)
= ki * dp[1] + (1-ki-ei)/m * dp[ fa[i] ]+ (1-ki-ei)/m * ∑dp[ son[i] ] + 1-ki-ei
可见dp[i]与dp[1],dp[fa[i]],dp[son[i]]有关。发现dp[fa[i]]和dp[son[i]]有关系,dp[son[i]]可通过dp[fa[i]]表示。那么设dp[i] = A[i] * dp[1] + B[i] * dp[fa[i]] + C[i]。
可以再列一个式子∑dp[son[i]] = ∑dp[j] = ∑(A[j] * dp[1] + B[j] * dp[i] + C[j])将该式子带入上式得:
dp[i] = ki * dp[1] + (1-ki-ei)/m * dp[ fa[i] ]+ (1-ki-ei)/m * ∑( A[j] * dp[1] + B[j] * dp[i] + C[j] ) + 1-ki-ei
最后得
(1-(1-ki-ei)/m*∑B[j])dp[i] = ( (1-ki-ei)/m*∑A[j] + ki)*dp[1] + (1-ki-ei)/m * dp[fa[i]] + (1-ki-ei)/m*∑C[j] + 1-ki-ei
对应之前设的dp[i] = A[i] * dp[1] + B[i]*dp[fa[i]] + C[i]可分别A[i],B[i],C[i]的递推式。
那么dp[1] = C[1]/(1-A[1]),通过式子得如果A[i] == 1,说明无解。
#include <stdio.h> #include <iostream> #include <map> #include <set> #include <list> #include <stack> #include <vector> #include <math.h> #include <string.h> #include <queue> #include <string> #include <stdlib.h> #include <algorithm> //#define LL __int64 #define LL long long #define eps 1e-9 #define PI acos(-1.0) using namespace std; const int INF = 0x3f3f3f3f; const int maxn = 10010; vector <int> edge[maxn]; double e[maxn],k[maxn]; double A[maxn],B[maxn],C[maxn]; //无根树转化为有根树,1号房间为根节点 bool dfs(int u, int pre) { int m = edge[u].size(); double tmp = 0; A[u] = k[u]; B[u] = (1-k[u]-e[u])/m; C[u] = 1-k[u]-e[u]; for(int i = 0; i < (int)edge[u].size(); i++) { int v = edge[u][i]; if(v == pre) continue; if(!dfs(v,u)) return false; A[u] += (1-k[u]-e[u])/m * A[v]; C[u] += (1-k[u]-e[u])/m * C[v]; tmp += (1-k[u]-e[u])/m * B[v]; } tmp = 1-tmp; if(fabs(tmp) < eps) return false; A[u] /= tmp; B[u] /= tmp; C[u] /= tmp; return true; } int main() { int test,n; scanf("%d",&test); for(int item = 1; item <= test; item++) { scanf("%d",&n); int u,v; for(int i = 0; i <= maxn; i++) edge[i].clear(); for(int i = 0; i < n-1; i++) { scanf("%d %d",&u,&v); edge[u].push_back(v); edge[v].push_back(u); } for(int i = 1; i <= n; i++) { scanf("%d %d",&u,&v); k[i] = u*1.0/100; e[i] = v*1.0/100; } printf("Case %d: ",item); if(dfs(1,-1) && fabs(A[1]-1) > eps) { printf("%.6lf\n",C[1]/(1-A[1])); } else printf("impossible\n"); } return 0; }