奖励关(NKOI 数学期望)

P2084【SCOI2008 Day1】奖励关
时间限制 : 10000 MS   空间限制 : 165536 KB

问题描述

你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出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
样例输入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

样例输出

样例输出1:
1.500000
样例输出2:
10.023470

提示

【数据规模】
1 <= k <= 100, 1 <= n <= 15,分值为[-10^6,10^6]内的整数。

 

#include
#include
using namespace std;
int k,n,BIT[20],s[20],maxx;
double f[105][17000],p[20];
int main()
{
    int i,t,j;
    scanf("%d%d",&k,&n);
    for(i=1;i<=n;i++)
    BIT[i]=1<<(i-1);
    
    maxx=1<<(n-1);
    for(i=1;i<=n;i++)
    {
        scanf("%lf",&p[i]);
        scanf("%d",&t);
        while(t!=0)
        {
            s[i]|=(1<<(t-1));
            scanf("%d",&t);
        }
    }
    for(i=k-1;i>=0;i--)
    for(j=maxx;j>=0;j--)
    for(t=1;t<=n;t++)
    if((j&s[t])==s[t])f[i][j]+=max((f[i+1][j|BIT[t]]+p[t]),f[i+1][j])/double(n);
    else f[i][j]+=f[i+1][j]/double(n);
    printf("%.6lf\n",f[0][0]);
    return 0;    

(代码有缺陷)

    这又是一个典型的数学期望问题,但是有趣的是n的取值范围,只有15,再考虑到再次出现了这种经典的条件性选择的限制(即类似于拓扑序列,在x出现之前必须先出现x1,x2,x3....),我们很容易想到记录每种奖励的出现情况(状态),再结合n<=15,这个奇妙的数据范围,很容易想到用状态压缩(二进制)来处理。

    于是只需要定义f[i][j]为第i天,奖励领取情况为j(把j看成二进制 )记录 奖励领取情况时到终点时期望获得的最大得分,然后,f[i][j]的值受到n个f[i+1][j']的影响(其中j'代表了这一天选取宝物之后,宝物领取情况改变后的状态)

    即f[i][j]=求和max((f[i+1][j']+p[k]),f[i+1][j])/n;(吃得起的情况下,吃与不吃)

    若吃不起,则f[i][j]+=f[i+1][j]/n(只好不吃)。

     这个地方需要注意的是,我们用到了一个小技巧,即是s[]数组的使用,把s[]数组存的数看做二进制,s[i]表示要吃到第i个宝物,必须先吃到那些宝物,需要吃到的宝物相应的二进制位置上记录为1。这个时候,只需要j&s[i],如果等于s[i],说明s[i]被完整地“包含”在了j中,说明符合吃的条件。

你可能感兴趣的:(数学期望)