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 ;
}
posted @ 2020-09-22 17:14  Soulist  阅读(150)  评论(0)    收藏  举报