lightoj1030——经典期望(直接逆向求期望)

题目链接:https://cn.vjudge.net/problem/LightOJ-1030

You are in a cave, a long cave! The cave can be represented by a 1 x N grid. Each cell of the cave can contain any amount of gold.

Initially you are in position 1. Now each turn you throw a perfect 6 sided dice. If you get X in the dice after throwing, you add X to your position and collect all the gold from the new position. If your new position is outside the cave, then you keep throwing again until you get a suitable result. When you reach the Nth position you stop your journey. Now you are given the information about the cave, you have to find out the expected number of gold you can collect using the given procedure.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case contains a blank line and an integer N (1 ≤ N ≤ 100) denoting the dimension of the cave. The next line contains N space separated integers. The ith integer of this line denotes the amount of gold you will get if you come to the ith cell. You may safely assume that all the given integers will be non-negative and no integer will be greater than 1000.

Output

For each case, print the case number and the expected number of gold you will collect. Errors less than 10-6 will be ignored.

Sample Input

3

 

1

101

 

2

10 3

 

3

3 6 9

Sample Output

Case 1: 101.0000000000

Case 2: 13.000

Case 3: 15

 

题目翻译:

你在一个山洞里,一个长长的山洞!洞穴可以由‎‎1 x N‎‎网格表示。洞穴的每个细胞可以包含任何数量的黄金。‎

‎最初,您处于‎‎位置 1‎‎。现在,每回合你扔一‎‎个完美的6‎‎面骰子。如果您在掷骰‎‎子后得到 X,您将‎‎X‎‎添加到您的位置,并从新位置收集所有黄金。如果你的新位置在洞穴外面,那么你继续投掷,直到你得到一个合适的结果。当你到达‎‎第N个位置‎‎时,你停止你的旅程。现在,您获得了有关洞穴的信息,您必须找出可以使用给定过程收集‎‎的黄金的预期‎‎数量。‎

‎输入‎

‎输入以整数‎‎T‎‎(=‎‎100)‎‎开头,表示测试用例的数量。‎

‎每个案例包含一个空白行和一个整数‎‎N (1 = N = 100)‎‎表示洞穴的尺寸。下一行包含‎‎N‎‎个空格分隔的整数。此行的第‎‎i‎‎整数表示,如果您来到‎‎i‎‎细胞,‎‎您将获得的黄金量。您可以放心地假设所有给定的整数都是非负数,并且没有整数大于‎‎1000‎‎。‎

‎输出‎

‎对于每个案例,打印案例编号和您将收集的黄金预期数量。小于‎‎10‎‎-6‎‎的错误将被忽略。

 

算是概率dp入门的经典题型,也是很经典的套路,反向递推。

状态:dp[i]表示从i走到n的黄金期望数量。

状态转移方程:dp[i]=(dp[i+1]+.......dp[i+6])/6+a[i];(一般情况)

然后结合考虑一下边界问题。

转移方程就变成了:

dp[i]=\frac{1}{6}\sum_{j=1}^{k}dp[i+j]+\frac{1}{6}\sum_{j=k+1}^{6}dp[i] \left \{ k=min(100-i,6)\right \}

\Rightarrow dp[i]=\frac{\sum_{j=1}^{k}dp[i+j]}{k}

初始化dp[i]=a[i];因为抛到外面再抛一次的话,仍然还是计算一次黄金数量,不能放在转移方程里面来计算。

最终状态转移方程就是:

dp[i]=(dp[i+1].....dp[i+k])/k+a[i];          ( k=min(6,n-i) )

其实我的第一想法是设状态为dp[i]表示从1到i的黄金期望数量,然后就可以正着推了,是不是很对。

代码写出来也过了样例,然后就交了一发,果断WA,那么为什么这样不行呢?

引用网上一个好的解释:

链接:https://www.cnblogs.com/6262369sss/p/8987216.html

为什么只有从后往前才会符合推理
因为从前往后的概率分布是不均匀的。
从a[i-6]到a[i]的概率并不是1/6,因为a[i-6]可以先到a[i-5],再到a[i],

所以实际上,a[i-6]到a[i]的 概率是低于其他值的,因为其他值到a[i]的概率是要先算上a[i-6]没有成功抵达a[i]的概率。

不能以1开始,因为状态方程只能为
a[i]=1/6*a[i+1]+ 1/6*a[i+2]+……+1/6*a[i+6]
假如我们令方程为
a[i]=1/6*a[i-1]+ 1/6*a[i-2]+……+1/6*a[i-6]
a[2]=a[1]+a[2]
a[3]=0.5a[1]+0.5a[2]+a[3]=a[1]+0.5a[2]+a[3]
那么
a[4]=1/3a[1]+1/3a[2]+1/3a[3]+a[4]=a[1]+0.5a[2]+1/3a[3]+a[4]
然而实际上a[4]的期望并不是这样
如果1到4。
从1开始,到2的概率为1/3,到3的概率为1/3加上2到3的概率,为0.5。
所以正确的是a[4]=a[1]+1/3a[2]+0.5a[3]+a[4]。

其实逆推可以保证一直是从里面推到外面而且概率是正确的。

因为先算3->4的得出dp[3]=a[3]+a[4]

再算2->4的,得到2->3(然后再到4),以及2->4,dp[2] = dp[3]/2 (2->3->4) + dp[4]/2(2->4) + a[2],这里面又用到3->4的期望dp[3]

然后类比这个处理过程,每次都从内到外的计算,就不会出现概率不对的情况了。

就像进行数运算时先考虑最里面的括号,算出来答案,然后再考虑次里面的括号,由内到外的算出答案。

代码实现: 

#include <iostream>
using namespace std;
const int maxn=105;
int a[maxn];
double dp[maxn];
int main(int argc, char** argv) {
	int T,n;
	scanf("%d",&T);
	for(int kcase=1;kcase<=T;++kcase){
		scanf("%d",&n);
		for(int i=1;i<=n;++i){
			scanf("%d",&a[i]);
			dp[i]=a[i];
		}
		for(int i=n;i>=1;i--){
			int rag=min(6,n-i);
			for(int j=1;j<=rag;++j){
				dp[i]+=1.0*(dp[i+j])/rag;
			}
		}
		printf("Case %d: %.7lf\n",kcase,dp[1]);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值