题目链接:P2473 [SCOI2008]奖励关
题意:
有n个宝物 每次等概率抛出其中之一
一共抛出k次
每个宝物有一个价值 和一个前提集合
只有集齐了集合中的所有宝物 才可以领取这个宝物
有n个宝物 每次等概率抛出其中之一
一共抛出k次
每个宝物有一个价值 和一个前提集合
只有集齐了集合中的所有宝物 才可以领取这个宝物
范围:1 <= k <= 100, 1 <= n <= 15,分值为[-106,106]内的整数
这个范围长得很dp呀
这个n长得很状压啊
这个n长得很状压啊
最初想法:
对于负价值宝物
我们计算它本身的贡献与它带来的期望贡献
来判定是否可取
对每一个宝物记录它自己的贡献
最后求和
对于负价值宝物
我们计算它本身的贡献与它带来的期望贡献
来判定是否可取
对每一个宝物记录它自己的贡献
最后求和
正解:逆向状压
2 ^ 15 = 32768
由于为什么不是正向 是为了避开在第i轮状态S不合法的情况
这就是本题的思维瓶颈
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]
三重循环 外面两重分别是这两维
第三重枚举第1~n个物品
若状态j中有物品k需要的所有物品
那么它的价值就是取或不取的最大值
max(f[i + 1][j], f[i + 1][j | (1 << k)] + w[k])
没有就只能不取
f[i + 1][j]
由于求的是期望
每个状态转移完除以n
每个状态转移完除以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 }