P2473 奖励关(压状+期望dp)

传送门

题目描述:我们玩k轮游戏,每一轮完全随机地从n个宝物中筛选出一个,每个宝物有相应的得分pi,可能为负数,我们可以选择它或者不选择它

每个宝物i可能有一些前驱宝物集合Si,只有Si中的宝物已经被选取过至少一次,才能选择宝物i,让我们求k轮游戏之后,我们期望的最大得分。

题解传送门

思路:n只有15,坑定是要压状的,定义f[i][s],表示在第i轮选取到的宝物集合为s的最大期望得分,我们遍历在该轮的某状态时的所有物品出现的情况,

并求出该情况下的最大期望,分为选取和不选取,而且必须要满足该物品前驱条件才能选取,求和,除去n后就得到该轮该状态下的最大期望。

但是正着推会出问题,必须倒着推。

题解:

概率是顺推,而期望需要逆推。

因为一个状态到后面状态的可能性是均分的(1/n),但是由从其他状态导入后一个状态时,由于题目条件的约束,

其他状态的各个概率并不是均分的。而且这题的条件约束是个乱七八糟的集合,毫无规律,所以正推不了。

状态转移方程:

可以选取则递推为上一轮不选取和选取中挑选:

dp[i][s]=max(dp[i+1][s],dp[i+1][s|(1<<(j-1))]+p[j]);

不可以选取:

dp[i][s]=dp[i+1][s];

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 10000005;
const int inf = 0x3f3f3f3f;
const int mod = 19260817;
int k, n;
double dp[105][40000];
int p[maxn],need[maxn];
int main() {
    //freopen("test.txt", "r", stdin);
    scanf("%d%d", &k, &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", p + i);
        int t; scanf("%d", &t);
        while (t) {
            need[i] |= 1 << (t-1);
            scanf("%d", &t);
        }
    }
    int maxs = (1 << n)-1;
    for (int i = k; i >= 1; i--) {//一定要逆推
        for (int s = 0; s <= maxs; s++) {
            for (int j = 1; j <= n; j++) {
                if ((s & need[j]) == need[j]) {//可以选取
                    dp[i][s] += max(dp[i + 1][s], dp[i + 1][s | (1 << (j - 1))] + p[j]);
                }
                else {//不可以选取
                    dp[i][s] += dp[i + 1][s];
                }
            }
            dp[i][s] /= n;
        }
    }
    printf("%.6f", dp[1][0]);//逆推到起点,一个都没选择的状态就是结果
    return 0;
}

 

posted @ 2021-04-29 09:03  cono奇犽哒  阅读(43)  评论(0编辑  收藏  举报