[补题记录] Atcoder Beginner Contest 314(E)

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];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值