CF696B Puzzles

本文探讨了一种结合随机DFS算法与期望动态规划的有趣问题,通过解析树形结构中的节点遍历顺序,计算每个节点被访问的时间戳期望值。文章详细介绍了算法原理,并附带了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

Barney lives in country USC (United States of Charzeh). USC has n cities numbered from 1 through n and n - 1 roads between them. Cities and roads of USC form a rooted tree (Barney’s not sure why it is rooted). Root of the tree is the city number 1. Thus if one will start his journey from city 1, he can visit any city he wants by following roads.

Some girl has stolen Barney’s heart, and Barney wants to find her. He starts looking for in the root of the tree and (since he is Barney Stinson not a random guy), he uses a random DFS to search in the cities. A pseudo code of this algorithm is as follows:

let starting_time be an array of length n 
current_time = 0 
dfs(v): 
current_time = current_time + 1 
starting_time[v] = current_time 
shuffle children[v] randomly (each permutation with equal possibility) 
// children[v] is vector of children cities of city v 
for u in children[v]: 
dfs(u) 

As told before, Barney will start his journey in the root of the tree (equivalent to call dfs(1)).
Now Barney needs to pack a backpack and so he wants to know more about his upcoming journey: for every city i, Barney wants to know the expected value of starting_time[i]. He’s a friend of Jon Snow and knows nothing, that’s why he asked for your help.

下面是译文:

Bob和Alice出去度蜜月,但Alice不慎走失,Bob在伤心过后,决定前去寻找Alice。
他们度蜜月的地方是一棵树,共有N个节点,Bob会使用下列DFS算法对该树进行遍历。

starting_time是一个容量为n的数组
current_time = 0
dfs(v):
current_time = current_time + 1
starting_time[v] = current_time
将children[v]的顺序随机排列 (每个排列的概率相同)
// children[v]v的直接儿子组成的数组
for u in children[v]:
dfs(u)

1是这棵树的根,Bob会从1出发,即运行dfs(1),现在他想知道每个点starting_time的期望值。

终于把这道题搞出来了... 汪汪大哭

真的是一道很好的期望dp题目

将到达每个节点$u$的期望时间戳记为$ans[u]$ 因为他是随机$dfs$所以他的兄弟节点会对他的期望时间戳产生贡献

横向来看 只看该节点与他的兄弟节点 将其父亲的儿子个数记为$m$ 也就是包括他自己的兄弟节点的个数

假设他在当前兄弟的$dfs$序中排名为$i$ 那么其余的每个兄弟节点排在他前面的概率是$\frac{i - 1}{m - 1}$

所以每个兄弟节点$bro$对他产生的贡献为$\frac{i - 1}{m - 1}\cdot size[bro]$ 

那么总贡献为 $\frac{i - 1}{m - 1}\cdot \sum_{bro \neq  u}size[bro]$ 

将$\sum_{bro \neq  u}size[bro]$ 记作$sum$ 易知$sum = size[fa] - size[u] - 1$ 减一是因为减去该子树的根节点

显然 节点$u$排名为$i$的情况的概率为$\frac{1}{m}$

所以$ans[u] = \frac{1}{m}\cdot \sum_{i = 1}^{m}\frac{i - 1}{m - 1}\cdot sum$

化简得 $ans[u] = \frac{1}{2}\cdot sum$

最后$ans[u] += ans[fa] + 1$ 加一是加上前面访问完了之后下一步走到$u$的步数

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
int size[N], sum[N], head[N], nex[2 * N], tov[2 * N], n, tot;
double ans[N];

void add(int u, int v) {
    
    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    head[u] = tot;
}

void dfs(int u, int fa) {
    
    size[u] = 1;
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fa) continue;
        dfs(v, u);
        size[u] += size[v];
    }
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fa) continue;
        sum[v] = size[u] - size[v] - 1;
    }
}

void Add_Edge( ) {
    
    scanf("%d",& n);
    for(int i = 1;i < n;i ++) {
        int fa;
        scanf("%d",& fa);
        add(fa, i + 1); add(i + 1, fa);
    }
    dfs(1, 1);
}

void Dfs(int u, int fa) {
    
    for(int i = head[u];i;i = nex[i]) {
        int v = tov[i];
        if(v == fa) continue;
        ans[v] = ans[u] + 1 + 0.5 * sum[v];
        Dfs(v, u);
    }
}

int main( ) {
    
    Add_Edge( );
    ans[1] = 1; Dfs(1, 1);
    for(int i = 1;i <= n;i ++) printf("%.1lf ", ans[i]);
}

转载于:https://www.cnblogs.com/Rubenisveryhandsome/p/9752921.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值