CF1267G

CF1267G

很有意思的题。

给定 (n) 张卡,第 (i) 张的价格为 (c_i)

你有两种方式获得卡牌:

  • 随机抽一张卡,花费为 (x),如果得到了一个已经有的卡,那么返还 (frac{x}{2})
  • 直接购买一张卡,花费为 (c_i)

求期望花多少集齐所有卡。

精度要求 (10^{-9})

(nle 100,sum c_ile 10^4)


Solution

注意到决策一定是先抽卡,然后再全买。

那么我们可以改变决策的方式,变成选择一种方式获得卡:

  • 按照规则随机抽卡。
  • 随机抽一张没有的卡并获得,花费为这张卡的花费。

我们发现这种情况下的最优解和原问题等价。

同时,由于每次抽卡均为随机抽卡,所以假设我们抽了 (i) 张不同的卡出来,那么这 (i) 张卡也将是随机的卡,换而言之原集中每种大小为 (i) 的子集被抽出的概率均相同!

现在考虑我们有 (i) 张卡,期望花费多少得到 (i+1) 张卡,不难发现期望花费为:((frac{n}{n-i}+1) imes frac{x}{2}),前面表示期望抽的次数,(+1) 表示最后一次抽会额外附带 (frac{x}{2}) 的贡献。

修改规则后,我们发生如果剩余的权值和为 (c),当前剩余 (k) 个元素,那么问题等价于期望以 (frac{c}{k}) 的贡献得到一个元素,否则以 ((frac{n}{k}+1) imes frac{x}{2}) 的贡献得到一个元素。

于是我们只需要比对这两者的大小,如果 (frac{c}{k}) 更大,那么以其期望产生贡献,否则以 ((frac{n}{k}+1) imes frac{x}{2}) 产生贡献。

将每一种情况发生的概率乘以贡献求和即为答案。

注意到所有子集的等价性,保留 (k) 个数使得权值和为 (c) 的概率是可以算出来的,于是这个题就做完了,复杂度 (mathcal O(n^2sum c))

  • 其实就是为了保证精度,边算边除以 (frac{1}{inom{n}{k}})(inom{n}{k}leftarrow inom{n}{k-1} imes 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
", ans ) ; 
	return 0 ;
}
原文地址:https://www.cnblogs.com/Soulist/p/13713365.html