3 3 1 2 1 3 0 0 100 0 0 100 3 1 2 2 3 0 0 100 0 0 100 6 1 2 2 3 1 4 4 5 4 6 0 0 20 30 40 30 50 50 70 10 20 60
Case 1: 2.000000 Case 2: impossible Case 3: 2.895522
题目大意:给定一个树状迷宫,初始在1号结点,在i号结点被杀死的概率为:k[i](百分比表示),走出迷宫的概率为:e[i](百分比表示),若未走出迷宫且未被杀死,则等可能的走向与其相连的一个结点,求走出这个迷宫的概率?
理解题以后觉得没法做。。。只理解了有向图,因为本题是无向边,所以父子结点的概率互相影响,立刻就忘了上一题也存在这种情况。。。
看了题解后才明白,应该按照期望设状态,而且需要在树上进行状态转移,最开始的状态转移方程最后化简即可消除影响(貌似就是树形DP+概率DP)
设dp[i]表示从i号结点走出迷宫时走过边数的概率,par[i]表示i号结点的父亲结点,son表示i号结点的儿子结点:
①若i结点为叶子结点,则 dp[i]=k[i]*dp[1]+e[i]*0+(1-k[i]-e[i])*(dp[par[i]]+1);
②若i结点不为叶子结点,则dp[i]=k[i]*dp[1]+e[i]*0+(1-k[i]-e[i])*((1/m)*(dp[par[i]]+1)+∑((1/m)*(dp[son[i]]+1)));【m表示与i相连的结点数】
由于每个结点都和1号结点以及其父亲结点有线性关系,所以设参数a[i],b[i],c[i],令dp[i]=a[i]*dp[1]+b[i]*dp[par[i]]+c[i];
将上式带入状态转移方程两式的可得:
①若i结点为叶子结点,则对比参数可得:
a[i]=k[i];
b[i]=1-k[i]-e[i];
c[i]=1-k[i]-e[i];
②若i结点不为叶子结点,设j为i结点的叶子结点:
则状态转移方程为:dp[i]=k[i]*dp[1]+e[i]*0+(1-k[i]-e[i])*((1/m)*(dp[par[i]]+1)+∑((1/m)*(dp[j]+1)));【m表示与i相连的结点数】
代入后得:dp[i]=k[i]*dp[1]+(1-k[i]-e[i])*((1/m)*(dp[par[i]]+1)+∑((1/m)*(a[j]*dp[1]+b[j]*dp[par[j]]+c[j]+1)));
化简合并同类项得:(1-(1-k[i]-e[i])/m*∑b[j])*dp[i]=(k[i]+(1-k[i]-e[i])/m*∑a[j])*dp[1]+(1-k[i]-e[i])/m)*dp[par[i]]+(1-k[i]-e[i])/m)*∑c[j]+1-k[i]-e[i];
对比参数可得:
a[i]= (k[i]+(1-k[i]-e[i])/m*∑a[j]) / (1-(1-k[i]-e[i])/m*∑b[j]) ;
b[i]= (1-k[i]-e[i])/m) / (1-(1-k[i]-e[i])/m*∑b[j]) ;
c[i]= ((1-k[i]-e[i])/m)*∑c[j]+(1-k[i]-e[i])) / (1-(1-k[i]-e[i])/m*∑b[j]) ;
而dp[1]=a[1]*dp[1]+b[1]*0+c[1] 可以推出:dp[1]=c[1]/(1-a[1]);
所以从叶子结点一次算出a[i],b[i],c[i],再求出dp[1];
#include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int MAXN=10005; const double EPS=1e-9; int n,k1,k2,k3,x,y,z; double a[MAXN],b[MAXN],c[MAXN],k[MAXN],e[MAXN],sum; vector<int> g[MAXN]; bool dfs(int u,int par) { int m=g[u].size(); double tt=0,tmp; a[u]=k[u]; tmp=b[u]=(1-k[u]-e[u])/m;//若u为叶子结点,则m==1,所以可以将所有的都初始化为该值 c[u]=1-k[u]-e[u]; int v; for(int i=0;i<g[u].size();++i) { v=g[u][i]; if(v!=par) { if(!dfs(v,u)) { return false; } a[u]+=tmp*a[v]; c[u]+=tmp*c[v]; tt+=tmp*b[v]; } } if(abs(1-tt)<EPS) {//若1-tt趋近于0,则无解 return false; } a[u]/=1-tt; b[u]/=1-tt; c[u]/=1-tt; return true; } int main() { int T,kase=0,s,f; scanf("%d",&T); while(kase<T) { scanf("%d",&n); for(int i=1;i<=n;++i) { g[i].clear(); } for(int i=1;i<n;++i) { scanf("%d%d",&s,&f); g[s].push_back(f); g[f].push_back(s); } for(int i=1;i<=n;++i) { scanf("%lf%lf",k+i,e+i); k[i]/=100; e[i]/=100; } printf("Case %d: ",++kase); if(dfs(1,0)&&abs(1-a[1]>EPS)) {//若1-a[1]趋近于0,则无解 printf("%.6lf\n",c[1]/(1-a[1])); } else { printf("impossible\n"); } } return 0; }