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

本文介绍了一种基于动态规划解决奖励关卡中宝物选择问题的方法。玩家需在限定次数内从多种宝物中选择以最大化得分,考虑宝物间依赖关系及可能出现的负分值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:点击打开链接

 

题意:

奖励关中,系统依次随机抛出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<cstdio>
#include<cstring>
#include<iostream>
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<p[k+1];j++){
			for(int t=1;t<=n;t++)
			 if((d[t]&j)==d[t]){
			 	f[i][j]+=max(f[i+1][j],f[i+1][j|p[t]]+v[t]);
			 }
			 else f[i][j]+=f[i+1][j];
			f[i][j]/=k;
		}
	}
	printf("%.6lf\n",f[1][0]);
	return 0;
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值