zoukankan      html  css  js  c++  java
  • 概率期望学习笔记

    简单概率期望题目总汇

    由于自己初学概率期望,学的都是简单题,就不分开写博客了.

    51Nod 1639绑鞋带

    非常入门的概率期望题目。但因为题目意思比较恶心.

    一共有(2 * n)个鞋头,第(i)次操作前还有(2 * n - (i - 1) * 2)个鞋头,由于我们选出一个后,它不能和自己绑,也不能和和它在同一条链上的绑。

    所以

    (f_{i + 1}= f_i * frac{2 * n - (i - 2) * 2 - 2}{2 * n - (i - 1) * 2 - 1})

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N = 1e3 + 3;
    double f[N];
    int n;
    double s;
    int main(){
    	scanf("%d",&n);
    	f[1] = 1;
    	for(int i = 1;i <= n;++i){
    		double last = (double)2 * n - (i - 1) * 2 - 1;
    		f[i + 1] = f[i] * ((last - 1.0) / last);
    	}
    	printf("%.6lf
    ",f[n]);
    	return 0;
    }
    

    BZOJ1419

    较简单概率期望DP,然而我还是不会做.

    概率期望DP在很多时候都应当倒推.

    这道题目网上题解的状态定义比较模糊,蒟蒻看不懂,在这里说一下自己的理解

    (f_{i,j})表示当前桌子上还有(i)个红牌未翻开(j)个黑牌未被翻开的,你的期望最优得分(再多得多少分)

    (f_{i,j} = (f_{i - 1,j} + 1)* frac{i}{i + j} + (f_{i ,j - 1} - 1) * frac{j}{i + j})

     #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    const int N = 5e3 + 3;
    double f[2][N];
    int cnt = 0;
    int b,r;
    int n;
    double ans;
    int main(){
    	scanf("%d%d",&r,&b);
    	for(int i = 1;i <= r;++i){
    		cnt ^= 1;f[cnt][0] = i;
    		for(int j = 1;j <= b;++j){
    			f[cnt][j] = max(0.0,1.0 * (f[cnt ^ 1][j] + 1) * (1.0 * i / (i + j))
    			 + 1.0 * ((f[cnt][j - 1] - 1) * (1.0 * j / (i + j))) );	
    		}
    	}
    	printf("%.6lf
    ",f[cnt][b] - 5e-7);
    	return 0;	
    }
    

    BZOJ3036

    同样是概率期望的入门题目.

    错误:入度统计反了

    我们依旧考虑建反图逆推(f_i)表示(i - n)的期望路径长度

    倒着DP即可

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<queue>
    #include<algorithm>
    using namespace std;
    const int N = 3e5 + 3;
    int tot,n,m;
    double f[N];
    int head[N],in[N],d[N];
    queue <int> q;
    struct edge{
    	int to;
    	int nxt;
    	int data;	
    }e[N];
    inline void add(int x,int y,int z){
    	e[++tot].data = z; 
    	e[tot].nxt = head[x];
    	e[tot].to = y;
    	head[x] = tot;	
    }
    inline void spfa(){
    	while(!q.empty()){
    		int k = q.front();
    		q.pop();
    		for(int i = head[k];i;i = e[i].nxt){
    			int y = e[i].to;
    			f[y] += (f[k] + 1.0 * e[i].data) / (1.0 * d[y]);
    		//	printf("%d %lf
    ",y,f[y]);
    			in[y]--;
    			if(!in[y]) q.push(y);
    		}
    	}
    	printf("%.2lf
    ",f[1]);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i = 1;i <= m;++i){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		add(y,x,z);
    		in[x]++;	
    		d[x]++;
    	}
    	q.push(n);
    	spfa();
    	return 0;	
    }
    

    HDU4576

    首先放一下自己的错误:滚动数组将(f_{cnt,j}=.....)写成了(f_{cnt,i} += .....)进行转移

    依旧是比较基础的概率期望的DP

    (f_{i,j})表示第(i)次操作完成之后在(j)位置的概率

    (f_{i,j} = f_{i - 1,j - w} * 0.5 + f_{i - 1,j + w} * 0.5)

    注意当(j - w)(j + w)越界的时候特判一下。细节见代码

    #include<iostream>
    #include<cctype>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int N = 205;
    double f[2][N];
    inline int read(){
    	int v = 0,c = 1;char ch = getchar();
    	while(!isdigit(ch)){
    		if(ch == '-') c = -1;
    		ch = getchar();
    	}
    	while(isdigit(ch)){
    		v = v * 10 + ch - 48;
    		ch = getchar();	
    	}
    	return v * c;
    }
    int main(){
    	while(1){
    		int n = read(),m = read(),l = read(),r = read();
    		if(n + m + l + r == 0) break;
    		for(int i = 0;i < 2;++i)
    			for(int j = 1;j <= n;++j) f[i][j] = 0;
    		int cnt = 0;f[0][1] = 1;
    		double ans = 0;
    		for(int i = 1;i <= m;++i){
    			int w = read() % n;
    			cnt ^= 1;
    			for(int j = 1;j <= n;++j){
    				f[cnt][j] = f[cnt ^ 1][j - w <= 0 ? n - (w - j) : j - w] * 0.5 + 
    				 f[cnt ^ 1][j + w > n ? j + w - n : j + w] * 0.5;	
    			}
    		}
    		for(int i = l;i <= r;++i) ans += f[cnt][i];
    		printf("%.4lf
    ",ans);
    	}
    	return 0;	
    }
    

    POJ2029

    这道题目没有犯SB错误

    我们想一下

    (f_{i,j})表示已经发现(i)种漏洞已经在(j)个系统发现了漏洞达到(f_{n,s})的期望天数

    借用这位大佬的图

    那么结合上面的图

    (f_{i,j} = (f_{i + 1,j + 1} + 1) *frac{(n - i) * (s - j)}{n * s} +(f_{i,j + 1} + 1)* frac{i*(s - j)}{n * s} + (f_{i + 1,j} + 1)*frac{(n - i)*j}{n*s}+ (f_{i,j} + 1) * frac{i * j}{n*s})

    之后我们设

    (P_1 = frac{(n - i) * (s - j)}{n * s})

    (P_2 = frac{(n - i)*j}{n*s})

    (P_3 =frac{i*(s - j)}{n * s})

    (P_4 = frac{i * j}{n*s})

    我们将上式化开

    (f_{i,j}*(1 - P_4) = f_{i + 1,j + 1}*P_1 + f_{i + 1,j} * P_2+f_{i,j + 1}*P_3 + (P_1+P_2+P_3+P_4))

    ((P_1+P_2+P_3+P_4) = 1)

    ((1 - P_4))除到右边就好了

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #define p1 (1.0 * (n - i) * (s - j) / (n * s))
    #define p2 (1.0 * (n - i) * j / (n * s))
    #define p3 (1.0 * i * (s - j) / (n * s))
    #define p4 (1.0 * i * j / (n * s))
    using namespace std; 
    const int N = 1e3 + 3;
    double f[N][N];
    int main(){
    	int n,s;
    	scanf("%d%d",&n,&s);
    	f[n][s] = 0;
    	for(int i = n;i >= 0;--i)
    		for(int j = s;j >= 0;--j)
    			if(i != n || j != s){
    			f[i][j] =  (
    				(f[i + 1][j + 1] * p1 + f[i + 1][j] * p2 + f[i][j + 1] * p3 + 1) / (1 - p4)
    			);
    		}
    	printf("%.4lf
    ",f[0][0]);
    	return 0;
    }	
    

    ZOJ3329

    一道非常好的概率期望DP

    我们设掷出和(s)的几率为(P_s)(特别的,将(a,b,c)那一组设为(P_0)),(f_i)为获得i分数的期望次数

    很明显的转移方程(倒推)

    (f_i = f_0*P_o + sum f_{i + k} * P_k + 1)

    之后我们发现了一个非常恶心的事情。就是每次转移都和(f_0)有关,而(f_0)是我们求得最终答案

    我们试着化简一下,因为每个(f)的式子都与(f_0)有关我们设

    (f_{i} = f_0*ai+bi)

    那么有

    (f_{i + k} = f_0*a_{i + k} + b_{i + k})

    我们将上式带回原式子

    (f_i = f_0*P_0+sum((f_0*a_{i + k} + b_{i + k})*P_k) + 1)

    试着整理一下

    (f_i = f_0*(P_0 + sum(a_{i + k} * P_k)) + sum b_{i + k} * P_k + 1)

    又因为

    (f_i = f_0 * a_i + b_i)

    我们就可以得到

    $a_i=P_0 + sum(a_{i + k} * P_k) $

    (b_i = sum b_{i + k}*P_k + 1)

    又因为

    (f_0 = f_0*a_0+b_0)

    所以

    (f_0 = frac{b_0}{(1 - a_0)})

    我们得到了(a)(b)的逆推式子,又知道(f_0)(a_0)(b_0)的关系,就好做

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<iostream>
    using namespace std;
    const int N = 1005;
    double f[N];
    double x[N],y[N],P[N];
    int k1,k2,k3,a,b,c,n;
    int main(){
    	int T;scanf("%d",&T);
    	while(T--){
    		memset(x,0,sizeof(x));
    		memset(P,0,sizeof(P));
    		memset(y,0,sizeof(y));
    		scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
    		int sum = k1 + k2 + k3;double Pi = 1.0 / k1 / k2 / k3;
    		for(int i = 1;i <= k1;++i)
    			for(int j = 1;j <= k2;++j)
    				for(int k = 1;k <= k3;++k) if(a != i || b != j || c != k) P[i + j + k] += Pi;
    		for(int i = n;i >= 0;--i){
    			x[i] = Pi,y[i] = 1;
    			for(int s = 3;s <= sum;++s)
    				x[i] += x[i + s] * P[s],y[i] += y[i + s] * P[s];
    		}
    		printf("%.15lf
    ",y[0] / (1 - x[0]));
    	}
    	return 0;
    }	
    
  • 相关阅读:
    2016.7.15
    2016.7.15
    2016.7.8
    2016.7.8
    2016.7.6
    2016.7.1--测评官网系列--手机行业
    2016.6.28--测评官网系列--牛奶行业
    2016.6.27
    Java中的Timer和TimerTask在Android中的用法
    Android 计时器Timer用法
  • 原文地址:https://www.cnblogs.com/wyxdrqc/p/10630402.html
Copyright © 2011-2022 走看看