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;
}