bzoj1076: [SCOI2008]奖励关(期望dp+状压dp)
1076: [SCOI2008]奖励关
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2989 Solved: 1557
[Submit][Status][Discuss]
Description
你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关。在这个奖励关里,系统将依次随机抛出k次宝物,
每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再吃)。
宝物一共有n种,系统每次抛出这n种宝物的概率都相同且相互独立。也就是说,即使前k-1次系统都抛出宝物1(
这种情况是有可能出现的,尽管概率非常小),第k次抛出各个宝物的概率依然均为1/n。 获取第i种宝物将得到Pi
分,但并不是每种宝物都是可以随意获取的。第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过
一次,才能吃第i种宝物(如果系统抛出了一个目前不能吃的宝物,相当于白白的损失了一次机会)。注意,Pi可
以是负数,但如果它是很多高分宝物的前提,损失短期利益而吃掉这个负分宝物将获得更大的长期利益。 假设你
采取最优策略,平均情况你一共能在奖励关得到多少分值?
Input
第一行为两个正整数k和n,即宝物的数量和种类。以下n行分别描述一种宝物,其中第一个整数代表分值,随
后的整数依次代表该宝物的各个前提宝物(各宝物编号为1到n),以0结尾。
Output
输出一个实数,保留六位小数,即在最优策略下平均情况的得分。
Sample Input
1 2
1 0
2 0
1 0
2 0
Sample Output
1.500000
HINT
【数据规模】
1<=k<=100,1<=n<=15,分值为[-10^6,10^6]内的整数。
/* 看到n很小可以状压 首先第一眼可以想到f[i][sta]表示到第i轮,物品选择状态为sta的最大期望值。 但是这样有个问题,可能到第i轮无法达到sta这个状态,但是也被当做了合法往后进行转移。 所以考虑倒退,这样状态就更改为f[i][sta]表示1~i-1轮能够到达sta这个状态,i到k轮的最大期望值。 这样就可以倒退,枚举下一个物品选不选进行转移了。 注意一点 这里求的是期望值,上面求的东西覆盖了第i轮取了所有n种宝物的情况 所以在每一个状态计算完之后,把f[i][sta]除以n即为期望平均值。 */ #include<iostream> #include<cstdio> #include<cstring> #define N 107 using namespace std; int n,k,cnt,need[N]; double val[16],f[N][1<<16]; inline int read() { int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int main() { int x; k=read();n=read(); for(int i=1;i<=n;i++) { scanf("%lf",&val[i]);x=read(); while(x) need[i]|=(1<<x-1),x=read(); } for(int i=k;i;i--) for(int sta=0;sta<=(1<<n)-1;sta++) { for (int j=1; j<=n; j++) if ((sta&need[j])==need[j]) f[i][sta]+=max(f[i+1][sta],f[i+1][sta|(1<<(j-1))]+val[j]); else f[i][sta]+=f[i+1][sta]; f[i][sta]/=(double)n; } printf("%.6lf\n",f[1][0]); return 0; }
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。