E
Problem/题意
有 N 个转盘,每次转动转盘可以获得点数,目标至少获得 M 点。
每个转盘转动需要 Ci 元,每个转盘有 Pi 个点数,点数为 Sij,等概率获得。
求至少获得 M 点的最小期望值。
Thought/思路
概率 dp。
定义 dp[i] 为,已经获得积分 i ,直到至少获得积分 m 的最小花费。显然我们可以从积分最大的情况开始转移,因为 dp[m] = 0 。
对于每一种积分情况(也就是每一个 i ),我们需要对每个转盘都计算一次转动该转盘的花费期望,在其中取最小值。
那么这个转动某个转盘的花费期望怎么计算呢?
我们发现,当选中某个点数 k 时,一定会花费 c[j] 元;而且如果当前积分 i 加上获得的点数 k 小于等于 m,则还需要再加上 dp[i + k] 。
依次将所有点数遍历一遍,累加即可。最后再除以这个转盘的点数个数即可。
需要注意的是,点数会有 0 的存在,所以对于 Ci,需要重新计算排除 0 的点数之后的平均转动花费。最后除以转盘点数,也需要排除 0 的个数。
Code/代码
#include "bits/stdc++.h"
double dp[107], c[107];
int n, m;
signed main() {
std::cin >> n >> m;
std::vector <std::vector <int>> s(n + 1);
for (int i = 1; i <= n; ++ i) {
int num, zero = 0; std::cin >> c[i] >> num;
for (int j = 1; j <= num; ++ j) {
int x; std::cin >> x;
if (x > 0)
s[i].push_back(x);
else
zero ++;
}
c[i] = 1.0 * c[i] * num / (num - zero);
}
for (int i = m - 1; i >= 0; -- i) {
double ans = 1e18; // 维护当前积分最小值
for (int j = 1; j <= n; ++ j) {
double tmp = 0; // 记录当前转盘最小花费
for (auto &k : s[j]) {
tmp += (i + k >= m ? 0 : dp[i + k]) + c[j];
}
tmp /= s[j].size();
ans = std::min(ans, tmp);
}
//std::cout << "i = " << i << " ans = " << ans << "\n";
dp[i] = ans;
}
std::cout << std::fixed << std::setprecision(10) << dp[0];
}