[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;
}
posted @ 2021-04-27 17:03  zkdxl  阅读(70)  评论(1编辑  收藏  举报