LG P2473 [SCOI2008]奖励关

题目链接:P2473 [SCOI2008]奖励关

题意:
有n个宝物 每次等概率抛出其中之一
一共抛出k次
每个宝物有一个价值 和一个前提集合
只有集齐了集合中的所有宝物 才可以领取这个宝物
范围:1 <= k <= 100, 1 <= n <= 15,分值为[-106,106]内的整数
 
这个范围长得很dp呀
这个n长得很状压啊
 
最初想法:
对于负价值宝物
我们计算它本身的贡献与它带来的期望贡献
来判定是否可取
对每一个宝物记录它自己的贡献
最后求和
 
正解:逆向状压
2 ^ 15 = 32768
由于为什么不是正向 是为了避开在第i轮状态S不合法的情况
这就是本题的思维瓶颈
刚刚纠结的负数问题 其实说白了就是取决于它后面的状态
所以逆推又避开了这个坑
显然二维dp 一维控制轮数 一维控制状态
三重循环 外面两重分别是这两维
第三重枚举第1~n个物品
若状态j中有物品k需要的所有物品
那么它的价值就是取或不取的最大值
max(f[i + 1][j], f[i + 1][j | (1 << k)] + w[k])
没有就只能不取
f[i + 1][j]
由于求的是期望
每个状态转移完除以n
附上代码:
 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstdlib>
 4 #include <cmath>
 5 using namespace std;
 6 const int N = 105;
 7 const int M = 16;
 8 int inc[M + 5];
 9 double w[M + 5];
10 int n, m;
11 double f[N][1 << M];
12  
13 int main(){
14     scanf("%d%d", &m, &n);
15     for(int i = 1, x; i <= n; i++){
16         scanf("%lf", &w[i]);
17         while(scanf("%d", &x) && x)
18             inc[i] |= (1 << x);
19     }
20     int lb;
21     for(int i = m; i >= 1; i--)
22     for(int j = 0; j < (1 << (n + 1)); j++){
23         for(int k = 1; k <= n; k++){
24             if((j & inc[k]) == inc[k]){
25                 f[i][j] += max(f[i + 1][j], f[i + 1][j | (1 << k)] + w[k]);
26             }
27             else f[i][j] += f[i + 1][j];
28         }
29         f[i][j] /= (1.0 * n);
30     }
31     printf("%.6lf", f[1][0]);
32     return 0;    
33 }
View Code
原文地址:https://www.cnblogs.com/hjmmm/p/9426673.html