AT_abc402_e [ABC402E] Payment Required 题解
思路
一道好期望 + 状压 DP 题。
首先看题,一眼期望 DP。再看数据范围,基本就可以确定解法十分暴力(\(1 \le n \le 8\))。
设 \(dp_{i, j}\) 代表当前已选集合为 \(i\),剩下 \(j\) 元的还可以得到的最大期望分数。
首先,成功的概率为 \(p\),失败的概率为 \(1 - p\)(这里假设 \(p = \dfrac{p_i}{100}\),\(p_i\) 为题目所给)。
由期望公式
可得成功的期望得分为 \(p \times (dp_{i + 2^k,j-c_k} + s_k)\)(\(i + 2^k\) 为解出来 \(k\) 后的状态),失败的期望得分为 \((1-p) \times dp_{i,j-c_k}\)。其中当前状态还没有解出 \(k\),且 \(c_k \le j\)(不然就买不起了)。
这里 \(dp_{i + 2^k,j-c_k} + s_k\) 的意思是解出 \(k\) 后,钱数少了 \(c_k\) 后还能获得的最大期望分数,以及解出 \(k\) 所得的分数 \(s_k\)。而 \(dp_{i,j-c_k}\) 的意思是没有解出 \(k\),钱数还是少了 \(c_k\) 后还能获得的最大期望分数。
那结合以上内容可以得到:
其中式子里的 \(k\) 满足上述条件。
那这样就倒着枚举 \(i\),正着枚举 \(j\)。
边界条件为 \(dp_{all, 0} = 0\),其中 \(all\) 指将这 \(n\) 个问题全部解决的状态。
最终答案 \(dp_{0, x}\),即还未做出一道题,也没用钱的期望钱数。
时间复杂度 \(\mathcal{O}(n \cdot x \cdot 2^n)\)。
注意:下标最好从 \(0\) 开始,本题解也默认下标从 \(0\) 开始。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 11, M = 5005;
int n, x;
int s[N], c[N];
double p[N], dp[(1 << N)][M];
int main()
{
scanf("%d%d", &n, &x);
for (int i = 0, P; i < n; i++)
{
scanf("%d%d%d", &s[i], &c[i], &P);
p[i] = P / 100.0; // 提前处理 p
}
for (int i = (1 << n) - 1; i >= 0; i--) // 倒序枚举 i!
{
for (int j = 1; j <= x; j++) // 正序枚举 j
{
for (int k = 0; k < n; k++)
{
if ((i & (1 << k)) || c[k] > j) continue; // 判断这种情况是否合法
dp[i][j] = max(dp[i][j], p[k] * (dp[i + (1 << k)][j - c[k]] + 1.0 * s[k]) + (1 - p[k]) * dp[i][j - c[k]]);
}
}
}
printf("%.9lf\n", dp[0][x]);
return 0;
}

浙公网安备 33010602011771号