| P2084【SCOI2008 Day1】奖励关 | |
|
问题描述
你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出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<iostream>
#include<cstdio>
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中,说明符合吃的条件。
在一款电子游戏中,玩家面临一个奖励关,需要根据宝物出现的概率和得分规则,选择最优策略以获得最高平均得分。文章通过数学期望模型,解决如何在已知宝物种类、得分和前提条件的情况下,最大化平均得分的问题。给出的代码示例虽然有缺陷,但展示了如何利用状态压缩和二进制技巧来处理这个问题。
&spm=1001.2101.3001.5002&articleId=82503878&d=1&t=3&u=92d5dd5add134996ba958da3c3414d87)
4045

被折叠的 条评论
为什么被折叠?



