题面:
有n个盒子,有个小球小球出现在每个盒子的概率为p[i],你现在可以问类似(小球是否在盒子{1,4,……,n}中?)(1-n的一个子集)的问题,问策略最优情况下,猜出小球所在盒子的猜测次数的数学期望。
思路:
因为任意一次询问和回答,都可以确定其中一半的球球集合包含目标球,另一半则不包含目标球。然后再对包含目标球的球球集合进一步划分,直到包含目标球的集合里只包含一个球,就可以百分百确定了。这样就得到了一个决策树(二叉形状),二叉决策树根节点到每个叶子的路到都是期中一种情况的解决方案,显然深度就是询问次数。 则有:
期望=∑(询问次数每种情况出现的概率)
=∑(叶子对应的深度它出现在盒子里的概率)。
而我们知道:这个公式 ∑(深度*元素出现的概率 ) 与某种编码方案的编码长度期望公式 相同。询问次数的期望最小也就是编码长度的期望最小。而解决这个问题的经典方法就是——哈夫曼树。
一句话总结:构造一棵哈夫曼树,然后有
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef pair<double,int>pa; 4 const int N=20000; 5 6 vector<int> G[N]; 7 double a[N],ans; 8 int n,cnt; 9 priority_queue<pa,vector<pa>,greater<pa> >q; 10 11 void dfs(int x, int d) { 12 if (G[x].size() == 0) ans += a[x] * d; 13 for (auto it : G[x]) { 14 dfs(it, d + 1); 15 } 16 } 17 int main() { 18 scanf("%d",&n); 19 for (int i=1;i<=n;i++) { 20 scanf("%lf", &a[i]); 21 if (a[i] == 0) { 22 n--; 23 i--; 24 continue; 25 } 26 q.push(pa(a[i], i)); 27 } 28 cnt=n; 29 while (q.size() >= 2) { 30 pa t1 = q.top(); 31 q.pop(); 32 pa t2 = q.top(); 33 q.pop(); 34 pa t3(t1.first + t2.first, ++cnt); 35 q.push(t3); 36 G[cnt].push_back(t1.second); 37 G[cnt].push_back(t2.second); 38 } 39 dfs(q.top().second, 0); 40 printf("%.7lf ", ans); 41 }