题目链接: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结尾。
输出格式:
输出一个实数,保留六位小数,即在最优策略下平均情况的得分。
输入输出样例
题解:
状压+期望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 ",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 }