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