【BZOJ】 [SCOI2008]奖励关-状压DP

传送门:点击打开链接

 

题意:

奖励关中,系统依次随机抛出k次宝物,每次选择吃或者不吃。宝物共n种,系统每次抛出这n种宝物的概率都相同且相互独立。 获取第i种宝物将得到Pi分,第i种宝物有一个前提宝物集合Si。只有当Si中所有宝物都至少吃过一次,才能吃第i种宝物。Pi可以是负数。假设采取最优策略,平均情况一共能在奖励关得到多少分值?

数据规模:

1<=k<=100,1<=n<=15(划重点),分值为【-1e6,1e6】内的整数。

题解:

数据量这么小,直接选择状压dp,以每一种宝物的2^i位来dp;利用二进制下的位或位与。

一般来说,概率顺推,期望倒推。因为概率要保证前一次出现存在这种情况来推算,而期望顺推难以判断前提是否成立。拿这道题来说,当第t种宝物落下时,顺推无法确定是否满足前提,也无法确定是否该吃负分的宝物,那么就来倒推一波~

得状态转移方程f[i][j](第i次下落已吃某几种宝物状态的最优解,某几种又j的二进制表示不为0的位数)

如可以吃 f[i][j]+=max(f[i+1][j],f[i+1][j|p[k]]+v[k]) (假设吃掉第k种宝物,v[k]为分值,p[k]二进制下只有第k位为1)

如不能吃 f[i][j]+=f[i+1][j];

因为求的是平均情况,最后除以一个n就好了。倒推得到f[1][0]即为答案。

代码如下

#include
#include
#include
using namespace std;
double f[16][65537];
int k,n,p[17],d[17],v[17];
int main(){
	scanf("%d%d",&k,&n);
	for(int i=1;i<=k+1;i++) p[i]=1<<(i-1);
	for(int e,i=1;i<=n;i++){
		scanf("%d%d",&v[i],&e);
		while(e!=0){
			d[i]+=p[e];
			scanf("%d",&e);
		}
	} 
	for(int i=n;i>=1;i--){
		for(int j=0;j

 

 

 

 

 

你可能感兴趣的:(状压DP)