1)题目意思
湖里有 n 条鱼。每天随机等概率选出一对鱼相遇(所有鱼对概率一样)。相遇后两条鱼必有一条被吃掉:
* 鱼 i 吃掉鱼 j 的概率是 a[i][j]
* 鱼 j 吃掉鱼 i 的概率是 a[j][i] = 1 - a[i][j]
一直重复,直到只剩 1 条鱼。要求输出每条鱼成为“最后幸存者”的概率,输出保留 6 位小数(题面说 6 位),每个概率之间空格分隔。
n <= 18,a[i][j] 是小数。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2)思路解析(状态压缩 DP,倒推集合概率)
关键观察
过程只会减少鱼的数量,不会增加,所以可以用“还活着的鱼集合”作为状态。
用 mask 表示当前还活着的鱼集合:
* mask 的第 i 位为 1 表示鱼 i 还活着(用 0..n-1 表示编号 1..n)
定义:
* dp[mask] 表示“某一时刻活着的鱼集合恰好是 mask 的概率”(从开始状态一路演化到这个状态的概率)
开始时所有鱼都活着:
* FULL = (1<<n) - 1
* dp[FULL] = 1
我们从“大集合”往“小集合”转移。因为一次相遇会死一条鱼,所以 mask 会去掉一个 bit。
转移怎么计算
在状态 mask 下,设当前活着的鱼数量为 k。每天等概率选一对活鱼相遇,鱼对数量是:
* pairs = k*(k-1)/2
从 mask 转移到 mask2 = mask without j(表示鱼 j 被吃掉),需要考虑:在 mask 中谁会吃掉 j。
如果 i 和 j 相遇,并且 i 吃掉 j,就会进入 mask2。所以从 mask 到 mask2 的概率增量为:
* 在所有鱼对中选到 (i,j) 的概率是 1/pairs
* 在选到 (i,j) 后,j 被吃掉的概率是 a[i][j]
因此:
* dp[mask2] += dp[mask] * ( sum over i in mask, i!=j of a[i][j] ) / pairs
这样我们枚举 mask,再枚举要死掉的鱼 j,把概率分给更小的集合。
最终答案
当只剩一条鱼 i 时,集合就是 mask = (1<<i),它的概率 dp[1<<i] 就是鱼 i 成为最后幸存者的概率。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3)代码