题解:AT_abc470_e [ABC470E] Concentration
题意简述
有一种游戏,桌上有
- 若
x = y ,获得x 的得分,去除这两张牌。 - 否则,生命值减少
1 ,放回这两张牌。
假定你看完牌后可以一直记住牌上的数,且牌的位置不变。问在最优策略下期望的得分是多少。
最优策略
根据贪心,应当立即配对已知可以配对的对,因为这样可以在不掉血的情况下得分。这一步过后,已知的牌上的数字两两不相等。若我们翻一张已知牌,我们无法得到更多信息,故最好的策略是翻一张未知牌,设得到
- 情况 1:若正好有一张已知牌上的数是
x_0 ,则我们直接翻这两张牌,得分x_0 且不掉血。 - 否则,翻出一张已知牌配对的概率为
0 ,不如翻出一张未知牌y_0 。- 情况 2.1:若
x_0 = y_0 ,得分x_0 且不掉血。 - 情况 2.2:否则,若正好有一张已知牌上的数是
y_0 ,我们应当在下一回合直接翻这两张牌,得分y_0 且掉一点血。 - 情况 2.3:否则,这两张牌都变为已知。
- 情况 2.1:若
代码实现
考虑 DP,设
由选取的随机性且上述策略和牌上的数无关,每次获得
边界条件是没血的时候
代码
本题 corner case 比较多,注意 官解说的记忆化卡常是假的。
#include <bits/stdc++.h>
using namespace std;
int n, l;
double dp[207][407][407];
double a, avg;
double get_dp(int i, int s, int t) { // 记忆化搜索
if (i == 0 || s > t)
return 0;
if (t == 0)
return (s / 2.0) * avg;
if (dp[i][s][t] >= -0.5)
return dp[i][s][t];
double ans = 0;
if (t > 0)
ans += (double)s / t * (get_dp(i, s - 1, t - 1) + avg);
if (t >= 2)
ans += (double)(t - s) / (t * (t - 1)) * (get_dp(i, s, t - 2) + avg);
if (t >= 2 && i > 1)
ans +=
(double)(t - s) * s / (t * (t - 1)) * (get_dp(i - 1, s, t - 2) + avg);
if (t >= 2)
ans += (double)(t - s) * (t - s - 2) / (t * (t - 1)) *
get_dp(i - 1, s + 2, t - 2);
return dp[i][s][t] = ans;
}
signed main() {
cin.tie(nullptr);
cout.tie(nullptr);
ios::sync_with_stdio(false);
cin >> n >> l;
for (int i = 1; i <= n; i++) {
cin >> a;
avg += a;
}
avg /= n;
for (int i = 0; i <= l; i++)
for (int s = 0; s <= (n << 1); s++)
fill(dp[i][s], dp[i][s] + (n << 1) + 1, -1);
cout << fixed << setprecision(9) << get_dp(l, 0, n << 1) << '\n';
}