P2473 [SCOI2008]奖励关 - 期望(神奇的倒推) - 状态压缩

大力模拟各种情况
k * n 的模拟出每次可能抛什么出来
设f[k][s] 为前k个物品状态为s时得分最大值
但是有问题啊,比如 113 和 133,状态都是f[3][101],难道我还要多开一维存下不同情况?,不行吧?

还是说取最大的?,这相当于后面选择的物品,对前面的状态强行选择了一下,但是并不是说113比133大,我就舍弃133,我舍弃133就相当于,走到这一步,后面直接都不走了,如果说我们要取最优解,显然可以取最大的,舍弃掉不优的,但是对于一种需要递推,或者是求期望的这种要求把所有可能都走一遍取平均值的,不能舍弃吧?舍弃这个状态,就好像求平均数的时候,你舍弃了几个比较小的数,那么无论如何这个平均数都是错误的吧?

这种转移感觉就很麻烦的,试着倒推,不是说拿东西么,我们把过程倒过来,扔东西,从第n个阶段每个阶段扔一个东西并获得收益,也就是说f[k][s]表示前k个状态为s时,k+1 ~ n轮获得的期望收益,而答案是f[1][0]

这代表了什么呢,为什么倒推的时候,是由前面的状态决定能不能“扔”?
实际上,假设有一种方案是01011吧,这个假设说明最后物品集合就是01011,只不过我们在预知了这个集合的情况下,把过程逆过来,那么我这个物品能不能扔,意味着这个物品(在正向过程中)能不能选,所以状态的设定是从k+1轮到最后的收益,但其物品集合却是第1 ~ k 轮选择的物品集合。从i + 1 到 i,只要f[i+1][j]中的j满足i的需求,就可以转移

有很多无用的状态也会发生转移,不过在取max的基础上这些转移无法到达最后,貌似状压有一部分题的转移都是这样的,一些状态本来不应该存在,但是在取max或者min的时候这些状态一定比那些应该存在的状态更不优,所以如果能证明出这点,不用考虑那么多直接转移就好

期望的和等于和的期望是划分阶段的关键条件,因为不同阶段的期望相加之后就是整个过程的期望,所以可以递推

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10;
struct object{
	double val;
	int si;
}ob[MAXN];
double ans,f[101][1<<15];
int n,k;

int main() {
	scanf("%d%d", &k, &n);
	for(int i=1; i<=n; i++) {
		cin >> ob[i].val;
		int si = 0;
		while(1) {
			int pos = 0;
			scanf("%d", &pos);
			if(!pos) break;
			si |= 1 << (pos-1);
		}
		ob[i].si = si; 	
	}
	for(int i=k; i; i--) {
		for(int j=0; j<1<<n; j++) {
			for(int x=1; x<=n; x++) {
				 int si = ob[x].si;
				 if((j&si) == si) {
				 	f[i][j] += max(f[i+1][j], f[i+1][j|(1<<(x-1))] + ob[x].val);
				 } else {
				 	f[i][j] += f[i+1][j];
				 }
			}
			f[i][j] /= n;
		}
	}
	printf("%.6lf", f[1][0]);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值