CF1267G
CF1267G
很有意思的题。
给定 \(n\) 张卡,第 \(i\) 张的价格为 \(c_i\)
你有两种方式获得卡牌:
- 随机抽一张卡,花费为 \(x\),如果得到了一个已经有的卡,那么返还 \(\frac{x}{2}\)
- 直接购买一张卡,花费为 \(c_i\)
求期望花多少集齐所有卡。
精度要求 \(10^{-9}\)
\(n\le 100,\sum c_i\le 10^4\)
Solution
注意到决策一定是先抽卡,然后再全买。
那么我们可以改变决策的方式,变成选择一种方式获得卡:
- 按照规则随机抽卡。
- 随机抽一张没有的卡并获得,花费为这张卡的花费。
我们发现这种情况下的最优解和原问题等价。
同时,由于每次抽卡均为随机抽卡,所以假设我们抽了 \(i\) 张不同的卡出来,那么这 \(i\) 张卡也将是随机的卡,换而言之原集中每种大小为 \(i\) 的子集被抽出的概率均相同!
现在考虑我们有 \(i\) 张卡,期望花费多少得到 \(i+1\) 张卡,不难发现期望花费为:\((\frac{n}{n-i}+1)\times \frac{x}{2}\),前面表示期望抽的次数,\(+1\) 表示最后一次抽会额外附带 \(\frac{x}{2}\) 的贡献。
修改规则后,我们发生如果剩余的权值和为 \(c\),当前剩余 \(k\) 个元素,那么问题等价于期望以 \(\frac{c}{k}\) 的贡献得到一个元素,否则以 \((\frac{n}{k}+1)\times \frac{x}{2}\) 的贡献得到一个元素。
于是我们只需要比对这两者的大小,如果 \(\frac{c}{k}\) 更大,那么以其期望产生贡献,否则以 \((\frac{n}{k}+1)\times \frac{x}{2}\) 产生贡献。
将每一种情况发生的概率乘以贡献求和即为答案。
注意到所有子集的等价性,保留 \(k\) 个数使得权值和为 \(c\) 的概率是可以算出来的,于是这个题就做完了,复杂度 \(\mathcal O(n^2\sum c)\)
- 其实就是为了保证精度,边算边除以 \(\frac{1}{\binom{n}{k}}\),\(\binom{n}{k}\leftarrow \binom{n}{k-1}\times \frac{n-k+1}{k}\)
\(Code:\)
#include<bits/stdc++.h>
using namespace std ;
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define D long double
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 100 + 5 ;
int n, c[N], m ;
D ans, t, f[N][10005] ;
D Min(D x, D y) { return min(x, y) ; }
signed main()
{
n = gi(), t = gi(), t /= 2.0 ;
rep( i, 1, n ) c[i] = gi(), m += c[i] ; f[0][0] = 1 ;
rep( i, 1, n ) drep( k, 1, i ) rep( j, c[i], m )
f[k][j] = (f[k][j] + f[k - 1][j - c[i]] * k / (1.0 * (n - k + 1)) ) ;
rep( k, 1, n ) rep( j, 0, m )
ans = (ans + f[k][j] * Min( ((D)n / (D)k + 1) * t, (D)j / ((D)k)) ) ;
printf("%.10Lf\n", ans ) ;
return 0 ;
}

浙公网安备 33010602011771号