[LuoguP2473] [SCOI2008]奖励关

洛谷传送门
题目描述

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出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 1 <= k <= 100 , 1 <= n <= 15 ,分值为[-106,106]内的整数。

解题分析

看到 n<=15 n <= 15 就知道大概是状压DP了, 但正向DP( dp[i][j] d p [ i ] [ j ] 表示第i轮状态j的最大值)似乎会出锅, 因为我们并不知道能不能转移到状态j。

所以我们倒推, 设 dp[i][j] d p [ i ] [ j ] 表示推到第i轮, 第1至i-1轮得到的状态为j, 就可以保证每个状态都可以到达了(因为即使无法到达也对最终状态 dp[1][0] d p [ 1 ] [ 0 ] 没有影响)。

状态转移方程为:
1.如果状态j允许k加入, 即 dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<k1)]+valk) d p [ i ] [ j ] + = m a x ( d p [ i + 1 ] [ j ] , d p [ i + 1 ] [ j | ( 1 << k − 1 ) ] + v a l k )
2.如果状态j不允许k加入, 即 dp[i][j]+=dp[i+1][j] d p [ i ] [ j ] + = d p [ i + 1 ] [ j ]

因为是求期望, 所以每轮下来 dp[i][j] d p [ i ] [ j ] 都应该除以n。最后 dp[1][0] d p [ 1 ] [ 0 ] 即为答案。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define R register
#define W while
using namespace std;
int req[20];
double dp[105][1 << 16];
int val[20];
int kind, tim;
int main(void)
{
    int a;
    scanf("%d%d", &tim, &kind);
    for (R int i = 1; i <= kind; ++i)
    {
        scanf("%d", &val[i]);
        W (233)
        {
            scanf("%d", &a);
            if(!a) break;
            req[i] ^= (1 << a - 1);
        }
    }
    int lim = (1 << kind) - 1;
    for (R int i = tim; i >= 1; --i)
    {
        for (R int j = 0; j <= lim; ++j)
        {
            for (R int k = 1; k <= kind; ++k)
            if((j & req[k]) == req[k])
            dp[i][j] += max(dp[i + 1][j], dp[i + 1][j | (1 << k - 1)] + val[k]);
            else dp[i][j] += dp[i + 1][j];
            dp[i][j] /= kind;
        }
    }
    printf("%.6lf", dp[1][0]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值