[CF1267G] Game Relics
\(\text{Problem}:\)Game Relics
\(\text{Solution}:\)
显然最优策略是先抽奖,然后直接购买。这里巧妙地转化题意,使得每次操作可以做以下决策:
-
随机从剩下的物品中选择一种,直接购买。
-
抽奖,直到获得一种当前还没有的物品。
根据我们的最优策略分析,上述的最优决策和题目给出的操作方式是等价的。
考虑当前已经获得了 \(i\) 个物品,通过抽奖的方式获得第 \(i+1\) 个物品的期望代价为 \((\frac{n}{n-i}+1)\cdot \frac{x}{2}\)。设该状态下剩余物品的个数和总价值分别为 \(x\) 和 \(k\),那么随机一种物品购买的期望代价为 \(\frac{x}{k}\)。则在该状态下操作的期望代价为 \(\min\{(\frac{n}{n-i}+1)\cdot \frac{x}{2},\frac{x}{k}\}\)(注意这里两个 \(x\) 的意义不同)。现在只需要求出到达每个状态的概率就可以计算得到答案。
不难发现,每个状态的代价只与剩余物品个数和价值总和有关。故考虑 \(dp\) 求解。设 \(f_{i,j}\) 表示已经获得 \(i\) 种物品,已获得的物品价值总和为 \(j\) 的概率。根据决策方式,已选的 \(i\) 种物品有 \(\binom{n}{i}\) 个集合,而到达每种集合的概率都是相等的,故可以直接背包转移。
总时间复杂度 \(O(n^{2}\sum c)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=105, M=10010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,X,W,c[N];
double ans,F[N][M];
signed main()
{
n=read(), X=read();
for(ri int i=1;i<=n;i++) c[i]=read(), W+=c[i];
F[0][0]=1;
for(ri int i=1;i<=n;i++)
{
for(ri int j=n;j;j--)
{
for(ri int k=W;k>=c[i];k--)
{
F[j][k]+=F[j-1][k-c[i]]*j/(n-j+1);
}
}
}
for(ri int i=0;i<n;i++)
{
for(ri int j=0;j<=W;j++)
{
ans+=F[i][j]*min(1.0*(W-j)/(n-i),(1.0*n/(n-i)+1.0)*(X*0.5));
}
}
printf("%.10lf\n",ans);
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。