期望dp初探。。。。

其实这个东西,就是根据 对于能从那个状态转移过来,及其转移代价之和除以概率的和。。。

因为期望的线性性质。。。

就可以算出来。。。。。
在这里插入图片描述

我这篇东西写的太垃圾了,推荐把题做了,不会做去别的地方看题解,不要看我的题解,,写的贼垃圾,,,,,(我文学造诣真的低。。。。)

例题A. [LightOJ-1038]
一句话题意:让你求一个数n,每次除他的一个因子,问你变成1的期望步数。。。。

其实你可以把这个东西当成图看。。。

然后呢,就会发现。。。直接拿与他相连的点把当前点算出来就好了。。。。

转移方程:
F ( n o w ) = s i g m a ( F ( T o ) + 1 ) / t o t t o t T o n o w F(now) = sigma (F(To) + 1)/tot 其中tot是因子数量,To为now的任意因子。。。。

#include<bits/stdc++.h>
#define MAXN 100000
using namespace std;

//题意:让你求一个数 每次除于他的因数,最后变成1的期望步数 
int T,n,cishu; 
double f[MAXN+5],sum,dx;

int main(){
	for(int i=2;i<=MAXN;i++){
		sum = 0;dx = -1;
		for(int j=1;j*j<=i;j++){
			if(i%j==0){
				sum+=f[i/j]+1,dx++;
				if(i/j!=j)sum+=f[j]+1,dx++;
			}
		}
		f[i] = (sum)/dx;
	}
	cin>>T;
	while(T--){
		cishu++;
		cin>>n;
		cout<<"Case "<<cishu<<": "; 
		printf("%.7f\n",f[n]);
	}
}

lightoj的题都挺好的,推荐板刷
例题B.POJ - 2096
一句话题意:一个软件有s个子系统,会产生n种bug。
某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中。
求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。
需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s,
属于某种类型的概率是1/n。(这个是摘抄某位大佬的。。。)

很显然,我们有状态:
f ( n o w , s u m ) n o w s u m f(now , sum)now个子系统中都找到了错误,且有sum种不同的错误

转移比较好想把
\frac{上}{下 }
出现在之前的系统,且出现在之前的bug
f ( n o w , s u m ) = n o w s u m b u g ( f ( n o w , s u m ) + 1 ) f(now , sum) = \frac{now*\frac{sum}{bug的数量 } * (f(now , sum)+1)}{系统的数量}
出现在之前的系统,且出现新的bug
f ( n o w , s u m ) = n o w b u g ( s u m 1 ) b u g ( f ( n o w , s u m 1 ) + 1 ) f(now , sum) = \frac{now*\frac{bug的数量-(sum -1)}{bug的数量 } * (f(now , sum - 1)+1)}{系统的数量}
出现在新的系统,且出现在之前的bug
f ( n o w , s u m ) = ( ( n o w 1 ) ) s u m b u g ( f ( n o w 1 , s u m ) + 1 ) f(now , sum) = \frac{(系统的数量-(now-1))*\frac{sum}{bug的数量 } * (f(now -1, sum )+1)}{系统的数量}
出现在新的系统,且出现新的bug
f ( n o w , s u m ) = ( ( n o w 1 ) ) b u g ( s u m 1 ) b u g ( f ( n o w 1 , s u m 1 ) + 1 ) f(now , sum) = \frac{(系统的数量-(now-1))*\frac{bug的数量-(sum -1)}{bug的数量 } * (f(now -1, sum -1)+1)}{系统的数量}

可以做了,,,,
markdown好恶心啊。。。。
好吧。。。。上面的要逆推。。。
一般来说,求期望是逆推,概率是正推。。。。mmp

注意,dp方程可能一次转移会转移回自己,注意移项。。。。。

注意,,,,可以移项。。。。(我之前忘记可以移项。。。。看题解懵逼了半天。。。。然后自己调,调的心态爆炸。。。)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAXN 1005
using namespace std;

double n,m;
double f[MAXN][MAXN];

int main(){
	int zz1,zz2;
	while(scanf("%d%d" , &zz1 , &zz2) == 2){
	memset(f , 1 , sizeof(f));
	n = zz1;
	m = zz2;
	for(int i = n ; i >= 0 ; i--){
		for(int j = m ; j >= 0 ; j--){
			if(i == n && j == m)continue;
			double tot = 1; 
			tot += (f[i + 1][j] * (n - i) * j) / (n * m);
			tot += (f[i][j + 1] * i * (m - j)) / (n * m);
			tot += (f[i + 1][j + 1] * (n - i) * (m - j)) / (n * m);
			f[i][j] = tot * n * m / (n * m - i * j);
		}
	}
	printf("%.4f\n" , f[0][0]);	
	}
}

例题3.ZOJ - 3329
题意:给你三个色子,然后呢,每个色子有ki个面。。。。有个计数器,每次只要没甩到给定的a,b,c计数器不会清零(此时不加abc的三倍) 不然就加上甩到的和的三倍,大于n就结束。。。
n<=500

注意到这个n很小。。。。而且维护次数的话做不了

于是就拿计数器作为状态。。。。
f ( n o w ) n o w f(now)当前计数器的值为now,一直甩到总结束的期望步数

然后我们又发现,k<=6
于是乎。。。。每次转移 O ( k 3 ) O(k^3)
似乎也没什么问题。。。。。

但会出现f(0)这种恶心的情况。。。。。于是乎,我们可以尝试通过把之前那个式子移项?算出来
f ( n o w ) = s i g a m a f ( n o w + d x ) + f ( 0 ) f(now) = sigama f(now + dx)*相应概率 + f(0) * 相应概率
易有
f ( n o w ) s i g a m a f ( n o w + d x ) = f ( 0 ) f(now) -sigama f(now + dx)*相应概率= f(0) * 相应概率
发现f(0)这里是个常数(也说不上是常数把。。。。。就是那种求不出来的值,就不好办了呀。。。。)

于是乎,要想办法怎么把f(0)搞出来。。。。
不妨分离一下系数。。。。。(意思就是拿f(0)作为主要的,其他的全部设成递推式。。。。)
(其实这里我也很懵逼。。。。。嘛,不过我的确是想要通过f函数的递推关系来搞。。。。但我没尝试啊。。。。毕竟推导比较恶心。。。)

不过这个题真的挺好的啊。。。。517牛逼~

#include<bits/stdc++.h>
#define MAXN 505
using namespace std;

int T;
int n,a,b,c;
double A[MAXN],B[MAXN],p[MAXN*5],dan,k[4];

/*
ÏÈÔ¤´¦Àí³öÿ¸öÊý³öÏֵĸÅÂÊ
È»ºóÔÙ¸ù¾ÝתÒÆʽ×ÓÀ´µÝÍÆ 
*/

void solve(){
	for(int now = n ; now >= 0 ; now--){
		A[now] = dan;
		B[now] = 1;
		for(int i = 1 ; i <= (k[1] + k[2] + k[3]) ; i++){
			A[now] += A[now + i] * p[i];
			B[now] += B[now + i] * p[i];
		}
	}
}

int main(){
	cin>>T;
	while(T--){
		memset(A , 0 , sizeof(A));
		memset(B , 0 , sizeof(B));
		memset(p , 0 , sizeof(p));
		cin>>n>>k[1]>>k[2]>>k[3]>>a>>b>>c;
		dan = (1.0 / (k[1] * k[2] * k[3]));
		for(int k1 = 1 ; k1 <= k[1] ; k1++){
			for(int k2 = 1 ; k2 <= k[2] ; k2++){
				for(int k3 = 1 ; k3 <= k[3] ; k3++){
					if(k1 == a && k2 == b && k3 == c)continue;
					p[k1 + k2 + k3] += dan;
				}
			}
		}
		solve();
		printf("%.16lf\n" , B[0] / (1 - A[0]));
	}
}
发布了80 篇原创文章 · 获赞 3 · 访问量 1745

猜你喜欢

转载自blog.csdn.net/qq_41567618/article/details/104833347
今日推荐