奖励关 题解

奖励关

可以去看我整理的学习笔记鸭~逃(

题意简述:共有 K K K 轮,有 n n n 种物品,每一轮出现每一种物品的概率 1 n \frac{1}{n} n1,物品可选可不选,对于选每一种物品,必须要在前面的轮先选给定的部分物品,每一种物品的价格可正可负。求 k k k 轮后按最优方案选择的期望价格。

数据范围: 1 ≤ K ≤ 100 1\leq K \leq 100 1K100 , 1 ≤ n ≤ 15 1≤n≤15 1n15

思路

首先看题,概率 d p dp dp 没得跑。

再看数据范围,哦豁!状压 d p dp dp

这道题不同的是需要倒推,因为正推的话有些情况在转移时选择宝物的概率并不是平均的(有宝物合集的限制),这样就会导致结果出现问题,且最终答案的状态表示十分麻烦 不要问我是怎么知道的!,因此选择倒推。

另外,因为 d p dp dp 的转移至于当前局前一局有关,所以可以用滚动数组优化一下。

还有就是,这里用到了一个状压小技巧。很多时候题目会给定条件限制,即枚举状态的前提条件。这里就再次用到了二进制小技巧。

这里就以这道题为例:

	while(~scanf("%d",&x) && x)
		num[i] += 1 << (x - 1);//用二进制数存储 宝物集合 

如果得到第 i i i 个宝物之前需要得到第 x x x 个宝物,我们就在 n u m [ i ] num[i] num[i] 的值加上 1 < < ( x − 1 ) 1 << (x - 1) 1<<(x1),最后 n u m [ i ] num[i] num[i]的值就是要得到第 i i i 个宝物的先决条件,枚举时的状态必须满足 n u m [ i ] num[i] num[i] 的要求。

其实这里就是在模拟二进制的形成,计算出满足题意的状态值,是不是很简单啊。

解题步骤

  • 先循环游戏轮数 i i i
  • 然后每轮枚举当前宝物的状态 j j j(第 x x x 位为 0 0 0 则未得到该宝物,反之得到);
  • 再枚举当局游戏抽到的宝物编号 k k k,判断是否满足条件(满足该宝物的先决条件)在拿 k k k 宝物和不拿之间选最大值加到当前状态;
  • 最终期望要乘上随机选择一个宝物的概率,因为是随机选择的啦。
  • 因为是倒推的,所以最后答案为游戏开始时,未得到任何一个宝物的情况

坑:因为用的滚动数组,所以每次记得清空 不要问我是怎么知道的!

其他细节讲解都在代码里啦~

完整代码

#include<cstdio>
#include<algorithm>
using namespace std;
int K,n,x,w[20],num[20];
double p,dp[2][1 << 15];
int main() {
	scanf("%d %d",&K,&n);
	p = 1.0 / n;//选择宝物的平均概率 
	for(int i = 1; i <= n; i ++) {
		scanf("%d",&w[i]);//分值 
		while(~scanf("%d",&x) && x)
			num[i] += 1 << (x - 1);//用二进制数存储 宝物集合 
	}
	int maxn = 1 << n;
	for(int i = K; i >= 1; i --) { //游戏轮数 
		for(int j = 0; j < maxn; j ++) {
			dp[i & 1][j] = 0;//初始化(清空上一次循环的值) 
			for(int k = 1; k <= n; k ++) {//枚举这轮抽到的宝物 
				if((j & num[k]) == num[k])//如果满足k宝物的 宝物集合要求,
					dp[i & 1][j] += max(dp[(i + 1) & 1][j | (1 << (k - 1))] + w[k],dp[(i + 1) & 1][j]);
					//满足最优策略,在拿k宝物和不拿之间选最大值 
				else dp[i & 1][j] += dp[(i + 1) & 1][j];//不满足的话,该状态及为上一次的状态,没有变化 
			}
			dp[i & 1][j] *= p;//最终期望要乘上随机选择一个宝物的概率 
		}
	} 
	printf("%.6lf",dp[1][0]);
	//因为是倒推的,所以最后答案为游戏开始时,未得到任何一个宝物的情况 
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值