签到题+手速铜牌题。当志愿者的时候看榜写的,事后发现想的太复杂了。和另一个打工人模第二个样例时,发现是个求期望的题,每个点可能的概率为 1 4 \frac{1}{4} 41 ,用第一次访问到每一个点需要的次数乘以该概率再累加即可。后来发现既然概率相同为什么不直接把所有需要的次数相加最后输出答案时再除以 n-1 。
不难发现不论以怎样的优先顺序进行遍历,最后得到的结果都是相同的。如样例 2 中,不论最开始从 1 号房间是向 2 / 5 号房间走,最后的总次数不变,因此无需考虑优先度,将所有结点均遍历一遍即可,当遍历到某结点发现已经遍历结束时,直接跳出 dfs 即可。
#include <bits/stdc++.h>
using namespace std;
const int N=105,M=N*2;
int h[N],cnt,ne[M],e[M];
bool vis[N];
int dis,n,sum;
double ans;
void add(int a,int b){
e[cnt]=b;
ne[cnt]=h[a];
h[a]=cnt++;
}
void dfs(int fa){
for(int i=h[fa];i!=-1;i=ne[i]){
//注意i不满足时的条件
int j=e[i];
if(vis[j])
continue;
vis[j]=true;
sum++;//表示已经遍历了多少个点
dis++;
ans+=dis;
if(sum==n){
//该点为最后一点
return;
}
dfs(j);
dis++;//走回需要一步 恢复现场
}
}
int main(){
int t;
cin>>t;
while(t--){
cin>>n;
cnt=0,dis=0,sum=0,ans=0;
memset(h,-1,sizeof(h));
memset(vis,0,sizeof(vis));
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
vis[1]=true;
sum=1;
dfs(1);
//cout<<sum<<" "<<ans<<endl;
ans/=(n-1);
printf("%.10lf\n",ans);
}
return 0;
}