奖励关[SCOI2008]

  题目链接:https://www.luogu.org/problemnew/show/P2473

 

题目描述

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。

宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。也就是说,即使前k-1 次系统都抛出宝物1(这种情况是有可能出现的,尽管概率非常小),第k次抛出各个宝物的概率依然均为1/n。

获取第 i 种宝物将得到Pi分,但并不是每种宝物都是可以随意获取的。第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i 种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。注意,Pi 可以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。

假设你采取最优策略,平均情况你一共能在奖励关得到多少分值?

输入输出格式

输入格式:

 

第一行为两个正整数k 和n,即宝物的数量和种类。以下n行分别描述一种

宝物,其中第一个整数代表分值,随后的整数依次代表该宝物的各个前提宝物(各

宝物编号为1到n),以0结尾。

 

输出格式:

 

输出一个实数,保留六位小数,即在最优策略下平均情况的得分。

 

输入输出样例

输入样例#1: 
1 2
1 0
2 0
输出样例#1: 
1.500000
输入样例#2: 
6 6
12 2 3 4 5 0
15 5 0
-2 2 4 5 0
-11 2 5 0
5 0
1 2 4 5 0
输出样例#2: 
10.023470

说明

1 <= k <= 100, 1 <= n <= 15,分值为[-106,106]内的整数。

 

 题解:

  状压+期望dp

  考虑对于一个状态s,若玩具i能被选(即(prei&s) == prei),则dpnow,s += max(dpnow+1,s,dpnow+1,s|(1<<(i-1))+ci)*1/n;否则dpnow,s += dfsnow+1,s*1/n 。

  转移方程不太好想,所以我用了记忆化搜索。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define LL long long
 7 #define RI register int
 8 using namespace std;
 9 const int INF = 0x7ffffff ;
10 const int N = 15 + 2 ;
11 const int K = 100 + 10 ;
12 
13 inline int read() {
14     int k = 0 , f = 1 ; char c = getchar() ;
15     for( ; !isdigit(c) ; c = getchar())
16       if(c == '-') f = -1 ;
17     for( ; isdigit(c) ; c = getchar())
18       k = k*10 + c-'0' ;
19     return k*f ;
20 }
21 int k, n ;  double dp[K][1<<N] ; int bit[N], c[N], pre[N] ;
22 
23 double dfs(int now,int s) {
24     if(dp[now][s] != -INF) return dp[now][s] ;
25     dp[now][s] = 0 ;
26     if(now == k) return dp[now][s] ;
27     for(int i=1;i<=n;i++) {
28 //        printf("pre[i]&s:%d pre[i]:%d\n",pre[i]&s,pre[i]) ;  // 之前下面一行没加括号时的输出调试
29         if((pre[i]&s) == pre[i]) {  // 这个括号很关键...因为位运算的优先级还不如等号qwq 
30             dp[now][s] += max(dfs(now+1,s),dfs(now+1,s|bit[i])+(double)c[i]) ;
31         }
32         else dp[now][s] += dfs(now+1,s) ;
33     }
34     dp[now][s] /= (double)n ;
35     return dp[now][s] ;
36 }
37 
38 int main() {
39     k = read(), n = read() ;
40     bit[1] = 1 ;
41     for(int i=2;i<=(n+1);i++) bit[i] = bit[i-1]<<1 ;  // 记录每个宝物代表的二进制数
42     memset(pre,0,sizeof(pre)) ;
43     for(int i=1;i<=n;i++) {
44         c[i] = read() ;
45         while(1) {
46             int x = read() ; if(!x) break ;
47             pre[i] += bit[x]  ;
48         }
49     }
50     for(int i=0;i<=k;i++) {
51         for(int s=0;s<bit[n+1];s++) dp[i][s] = -INF ;
52     }
53     dfs(0,0) ; printf("%.6lf",dp[0][0]) ;
54     return 0 ;
55 }

 

posted @ 2018-03-24 09:52  zubizakeli  阅读(177)  评论(0)    收藏  举报