HDU 4035 Maze(概率dp,一次函数递推)
题目大意
有一个树形的迷宫,从节点1开始走,每个节点都有\(k_i\)的概率回到1,\(e_i\)的概率逃出迷宫,每个房间有\(m\)条边相连,求走出迷宫所需步数的期望。
解题思路
设走出迷宫步数的期望为\(E_i\),父亲为\(F_i\),儿子为\(S_i\),相邻的节点个数为\(m\),则\(E_1\)即为所求。
对于叶子节点来说,可以得到: \(E_i = k_i \times E_1 + (1-k_i-e_i) \times (1 + E_{F_i})\)。
即\(E_i = k_i \times E_1 + (1-k_i-e_i) \times E_{F_i} + 1-k_i-e_i\)。
对于非叶子节点来说,可以得到: \(E_i = k_i \times E_1 + \frac{(1-k_i-e_i)}{m} \times (1 + E_{F_i} + \sum (E_{S_i} + 1))\)。
即\(E_i = k_i \times E_1 + \frac{(1-k_i-e_i)}{m} \times E_{F_i} + \frac{(1-k_i-e_i)}{m} \times \sum E_{S_i} + 1-k_i-e_i\)。
我们发现每个式子中都有\(E_1\)这个变量,这是不便于我们递推的,我们可以尝试把递推的值转为一次函数。
设\(E_i = A_i \times E_1 + B_i \times E_{F_i} + C_i\), 那么将这个式带入非叶节点的公式替换掉\(E_{S_i}\)可得:
\(E_i = k_i \times E_1 + \frac{(1-k_i-e_i)}{m} \times E_{F_i} + \frac{(1-k_i-e_i)}{m} \times (\sum (A_{S_i} \times E_1 + B_{S_i} \times E_i + C_{S_i})) + 1-k_i-e_i\)
整理得: \((1 - \frac{(1-k_i-e_i)}{m} \times \sum B_{S_i}) \times E_i = (k_i + \frac{(1-k_i-e_i)}{m} \times \sum A_{S_i}) \times E_1 + \frac{(1-k_i-e_i)}{m} \times E_{F_i} + 1-k_i-e_i + \frac{(1-k_i-e_i)}{m} \times \sum C_{S_i}\)
\(A_i = (k_i + \frac{1-k_i-e_i}{m} \times \sum A_{S_i}) / (1 - \frac{1-k_i-e_i}{m} \times \sum B_{S_i})\)
\(B_i = (\frac {1-k_i-e_i}{m}) / (1 - \frac{1-k_i-e_i}{m} \times \sum B_{S_i})\)
\(C_i = (1-k_i-e_i + \frac {1-k_i-e_i}{m} \times \sum C_{S_i}) / (1 - \frac{1-k_i-e_i}{m} \times \sum B_{S_i})\)
这样我们通过递推出\(A_1, B_1, C_1\)就能得到答案了。
代码
const int maxn = 1e5+10;
const int maxm = 2e6+10;
double k[maxn], e[maxn];
vector<int> g[maxn];
int n;
double a[maxn], b[maxn], c[maxn];
void init() {
clr(a, 0); clr(b, 0); clr(c, 0);
for (int i = 0; i<=n; ++i) g[i].clear();
}
bool dfs(int u, int p) {
double m = g[u].size();
double pi = (1-k[u]-e[u])/m;
double sum1 = k[u], sum2 = pi, sum3 = 1-k[u]-e[u], sum = 0;
for (auto v : g[u]) {
if (v==p) continue;
if (!dfs(v, u)) return 0;
sum1 += pi*a[v];
sum += pi*b[v];
sum3 += pi*c[v];
}
sum = 1-sum;
if (fabs(sum)<eps) return 0;
a[u] = sum1/sum;
b[u] = sum2/sum;
c[u] = sum3/sum;
return 1;
}
int main(void) {
//IOS;
int __; cin >> __;
int kase = 0;
while(__--) {
cin >> n;
init();
for (int i = 1; i<n; ++i) {
int a, b; scanf("%d%d", &a, &b);
g[a].push_back(b);
g[b].push_back(a);
}
for (int i = 1; i<=n; ++i) {
scanf("%lf", &k[i]); k[i] /= 100.0;
scanf("%lf", &e[i]); e[i] /= 100.0;
}
if (dfs(1, -1) && fabs(1-a[1])>eps) printf("Case %d: %.6f\n", ++kase, c[1]/(1-a[1]));
else printf("Case %d: impossible\n", ++kase);
}
return 0;
}