LG2473 [SCOI2008] 奖励关

Problem

LG2473 [SCOI2008] 奖励关

Analysis

自己的思考

我发现状态很好设计:\(dp_{i,j}\) 为前 \(i\) 关宝物是否吃的状态为 \(j\) 的期望。

然后转移我就开始乱了,期望的转移我总是想不到。

最后我考虑先计算权值,再除以所有的方案数,但转移还是有问题。

题解的思考

题解设的状态一开始与我一样,但是它发现这样的 dp 是不满足最优子结构,因为可能存在当下没有选择某个负数,导致长远利益损失。(这一点题目中强调了,可我却没看见)

于是它设 \(dp_{i,j}\)从后往前考虑的第 \(i\) 关宝物是否吃的状态为 \(j\) 的期望。

转移就是先枚举前面关的状态 \(s\),再枚举这一关选择了哪一个宝物 \(j\)。先检查合法性(\(j\) 的前置宝物集合是否为 \(s\) 的子集),若不合法,只考虑不选的情况:\(dp_{i,s}\gets dp_{i,s}+dp_{i+1,s}\);否则,选与不选都要考虑:\(dp_{i,s}\gets dp_{i,s}+\max(dp_{i+1,s},dp_{i+1,s|1<<j-1})\)

每次再将结果除以 \(n\) 算概率。

答案就是 \(dp[1][0]\)

知识点/技巧总结

本题考察了选手对于期望的理解和使用 dp 的前提条件的理解。

AC Code

点击查看代码
// Problem:
#include <bits/stdc++.h>
#define ll long long 
#define ull unsigned long long 
#define i128 __int128
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define mk make_pair
#define INF 0x3f3f3f3f
#define INFx 0x3f3f3f3f3f3f3f3f
using namespace std;

const int N = 16, M = 110;

int k, n;
int p[N], s[N];
double dp[M][1 << N];

int main() {
    freopen("sample.in", "r", stdin);
    freopen("sample.out", "w", stdout);
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);

    cin >> k >> n;
    for (int i = 1, x; i <= n; i ++) {
        cin >> p[i];
        while (cin >> x, x) s[i] |= 1 << x - 1;
    }
    
    for (int t = k; t >= 1; t --) {
        for (int i = 0; i < (1 << n); i ++) {
            for (int j = 1; j <= n; j ++) {
                if ((s[j] & i) == s[j]) dp[t][i] += max(dp[t + 1][i], dp[t + 1][i | (1 << j - 1)] + p[j]);
                else dp[t][i] += dp[t + 1][i];
            }
            dp[t][i] /= n; 
        }
    }

    cout << fixed << setprecision(6) << dp[1][0] << '\n';

    return 0;
}
posted @ 2026-02-24 16:18  KenopsiaMind  阅读(0)  评论(0)    收藏  举报