【CF1267G】Game Relics(期望)
题目链接
- 有 \(n\) 个物品,第 \(i\) 个物品直接购买的价格为 \(c_i\)。
- 除了直接购买,你还可以以 \(x\) 的代价等概率抽取一件物品,如果已经获得过抽到的物品则退还 \(\frac x2\)。
- 求获得所有物品的最小期望花费。
- \(1\le n\le100\),\(c_i\ge x\),\(\sum c_i\le 10^4\)
先抽卡再购买
考虑我们一定是选择先抽卡再购买。
因为假如我们先购买再抽卡,只会让抽取到新物品的概率变小,而抽重复退还的 \(\frac x2\) 又比购买花费更小,因此肯定不优。
假设当前已经有 \(i\) 个物品,拥有的物品价值和为 \(j\)(并设所有物品价值总和为 \(t\))。
则,此时抽取出一个新物品的概率为 \(\frac{n-i}n\),也就是说期望次数为 \(\frac n{n-i}\),那么期望代价就是 \((\frac n{n-i}+1)\frac x2\)。而剩余物品总价值为 \(t-j\),也就是说抽取出的物品的期望价值为 \(\frac{t-j}{n-i}\)。
显然,如果期望代价比期望价值更大,那么我们肯定不会再选择去抽卡,而是直接购买了。
实际上可以利用这两个式子再次证明开始的结论:\((\frac n{n-i}+1)\frac x2 > \frac{t-j}{n-i}\Leftrightarrow (2n-i)\frac x2>t-j\),则随着 \(i\) 的增大,左式减少 \(\frac x2\),右式减少 \(c_{k}\),因为右式减少得更多,不等式必然依旧成立。
随机购买
如果直接考虑什么时候转成全部购买,需要保证这一次抽卡代价更大,且上一次抽卡代价更小,比较麻烦,复杂度偏高。
所以需要再来一个转化,就是即便确定了要直接购买了,我们不一次性全买下来,而是随机去一个一个购买,容易发现这与原问题等价。
现在既然完全随机了,那么出现任意一种局面的概率就可以直接计算了。
仍然假设当前已经有 \(i\) 个物品,拥有的物品价值和为 \(j\),那么这种局面出现概率就是 \(\frac1{C_n^i}\),可以简单背包求出此类局面个数。
而这种局面的期望代价就是先前提到的两种期望代价的较小值,即 \(\min\{(\frac n{n-i}+1)\frac x2,\frac{t-j}{n-i}\}\)。
代码:\(O(n^2\sum c_i)\)
#include
#define Tp template
#define Ts template
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100
#define V 10000
#define DB double
using namespace std;
int n,x,c[N+5];DB g[N+5][V+5],C[N+5][N+5];
int main()
{
RI i,j,k;for(scanf("%d%d",&n,&x),i=1;i<=n;++i) scanf("%d",c+i);
for(C[0][0]=i=1;i<=n;++i) for(C[i][0]=j=1;j<=i;++j) C[i][j]=C[i-1][j-1]+C[i-1][j];//预处理组合数
DB t=0;for(g[0][0]=i=1;i<=n;t+=c[i++]) for(j=i-1;~j;--j) for(k=t;~k;--k) g[j+1][k+c[i]]+=g[j][k];//背包预处理每种局面数
DB ans=0;for(i=0;i^n;++i) for(j=0;j<=t;++j) ans+=min((1.0*n/(n-i)+1)*x/2,(t-j)/(n-i))*g[i][j]/C[n][i];//枚举局面计算期望
return printf("%.12lf\n",ans),0;
}