• 概率期望


    •期望计算方法:每种事件的值乘该事件发生的概率

    $ E(X)=∑x_i P(X=x_i )$

    随机的抛掷一枚骰子,所得骰子的点数的数学期望为 (1+2+3+4+5+6)/6 =3.5

    例1:POJ 2096找bug

    Description

    •有s个系统,n种bug,小明每天找出一个bug,可能是任意一个系统的,可能是任意一种bug,即是某一系统的bug概率是1/s,是某一种bug概率是1/n。求他找到s个系统的bug,n种bug,需要的天数的期望。

    Solution

    [dp[i][j]表示已经找到i种bug,并存在于j个子系统中,要达到目标状态的天数的期望。\ 显然,dp[n][s]=0,因为已经达到目标了。而dp[0][0]就是我们要求的答案。\ dp[i][j]状态可以转化成以下四种:\ dp[i][j] 发现一个bug属于已经找到的i种bug和j个子系统中——p1=frac{j}{s}*frac{i}{n}\ dp[i+1][j] 发现一个bug属于新的一种bug,但属于已经找到的j个子系统——p2=frac{j}{s}*frac{n-i}{n}\ dp[i][j+1] 发现一个bug属于已经找到的i种bug,但属于新的子系统——p3=frac{s-j}{s}*frac{i}{n}\ dp[i+1][j+1]发现一个bug属于新的一种bug和新的一个子系统——p4=frac{s-j}{s}*frac{n-i}{n}\ 由上述期望计算方法\ dp[i,j] = p1*dp[i,j] + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] + 1;\ dp[i,j] = ( 1 + p2*dp[i+1,j] + p3*dp[i,j+1] + p4*dp[i+1,j+1] )/( 1-p1 ) = ( n*s + (n-i)*j*dp[i+1,j] + i*(s-j)*dp[i,j+1] + (n-i)*(s-j)*dp[i+1,j+1] )/( n*s - i*j ]

    poj玄学data %lf过不去,%f才能过。。。诡异

    #include <iostream>
    #include <cstdio>
    using namespace std;
    #define siz 1005
    int n,s;
    double dp[siz][siz];
    int main(){
        scanf("%d%d",&n,&s);
        dp[n][s]=0.0;
        for(int i=n;i>=0;i--){
            for(int j=s;j>=0;j--){
                if(i==n&&j==s)continue;
                dp[i][j]=(n*s+(n-i)*j*dp[i+1][j]+i*(s-j)*dp[i][j+1]+(n-i)*(s-j)*dp[i+1][j+1])/(n*s-i*j);
            }
        }
        printf("%.4f
    ",dp[0][0]);
        return 0;
    }
    

    例1:bzoj2134 单选错位

    如果a>b,那么有b/a的概率选项在1...b之间,这是又有1/b的概率正确,因此期望为1/a;

    如果a<b,那么有a/b的概率选项在1...a之间,这是又有1/a的概率正确,因此期望为1/b。

    a=b,显然,总的期望就是1/max(a,b)。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long LL;
    const int p=100000001;
    int n,a,b,c,x,y,st;
    double ans=0;
    int main() {
        scanf("%d%d%d%d%d",&n,&a,&b,&c,&x);st=y=x;
        for(int i=2;i<=n;i++) {
            x=((LL)x*a+b)%p;
            ans+=1.0/max(x%c+1,y%c+1);y=x;
        }
        printf("%.3lf
    ",ans+1.0/max(x%c+1,st%c+1));
        system("pause");
        return 0;
    }
    
    

    例2:排队

    Description

    有n个人排成一列,, 每秒中队伍最前面的人p的概率走上电梯( 一旦走上就不会下电梯),或者有1-p 的概率不动

    问你T秒过后,, 在电梯上的人的数量的期望.

    Solution

    dp概率

    [设f[ i ] [ j ]表示前 i 秒剩下n个人 j 个人的概率\ f[i][j]=f[i-1][j-1]*p+f[i-1][j]*(j==n?1:(1-p))\ 显然n个人都没了就不乘 ]

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int t,n;
    double p,f[2050][2050],ans=0;//i秒过后,电梯上有j个人的概率
    int  main() {
        scanf("%d%lf%d",&n,&p,&t);
        f[0][0]=1;
        for(int i=1;i<=t;i++){
            f[i][0]=f[i-1][0]*(1-p);
            for(int j=1;j<=n;j++)
                f[i][j]=f[i-1][j-1]*p+f[i-1][j]*(j==n?1:(1-p));
        }
        for(int i=1;i<=t;i++)
            ans+=f[t][i]*i;
        printf("%.6lf
    ",ans);
        // system("pasue");
        return 0;
    }
    

    dp期望

    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=t;j++) f[i][j]=(f[i-1][j-1]+1)*p+f[i][j-1]*(1-p);
    	printf("%.6lf",f[n][t]);
    

    例3: Osu(1)

    [{(x+1)}^2-x^2=2*x+1\ 设f[i]表示线性期望,g[i]表示答案\ 则有f[i]=(f[i-1]+1)*p,g[i]=g[i-1]+(2*f[i-1]+1)*p\ ]

    当然这里不用数组就可

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int n;
    double x=0.0,p,ans;
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            scanf("%lf",&p);
            ans+=(2*x+1)*p;
            x=(x+1)*p;
        }
        printf("%lf
    ",ans);
        system("pause");
        return 0;
    }
    

    变式1

    平方变立方

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int n;
    double x=0.0,y=0.0,p,ans;
    int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) {
            scanf("%lf",&p);
            ans+=(3*y+3*x+1)*p;
            y=(y+2*x+1)*p;
            x=(x+1)*p;
        }
        printf("%.1lf
    ",ans);
        return 0;
    }
    

    变式2

    len记录线性dp,ans记录期望

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int n;
    char c;
    long double x=0.0,ans=0.0,len;
    int main() {
        scanf("%d",&n);c=getchar();
        for(int i=1;i<=n;i++) {
            c=getchar();
            if(c=='o') ans+=2*len+1,len++;
            else if(c=='x') len=0;
            else ans+=len+0.5,len=(len+1)/2;
        }
        printf("%.4Lf
    ",ans);//大写L
        // system("pause");
        return 0;
    }
    

    例4:奖励关

    状压期望,反着跑,因为正着跑有些状态不一定能到(k不够)

    详解blog

    #include <cstdio>
    #include <iostream>
    using namespace std;
    int k,n,s[1<<16],p[20],ss;
    double f[105][1<<16];
    inline int read() {
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    int main() {
    	k=read();n=read();	
    	for(int i=1;i<=n;i++) {
    		p[i]=read();
    		while(1) {
    			ss=read();
    			if(ss==0)break;
    			s[i]|=(1<<(ss-1));
    		} 
    	}
    	for(int i=k;i>=1;i--){
    		for(int state=0;state<(1<<n);state++) {
    			for(int j=1;j<=n;j++) {
    				if((s[j]&state)!=s[j]) f[i][state]+=f[i+1][state]; //不满足
    				else  f[i][state]+=max(f[i+1][state],f[i+1][state|1<<(j-1)]+p[j]);
    			}
    			f[i][state]/=n;
    		}
    	}
    	printf("%.6lf
    ",f[1][0]);		
    	return 0;
    }
    

    例5:绿豆蛙的归宿(图上期望)

    拓扑序,

    法一:正着跑,out记录出边

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    typedef long long ll;
    #define N 100005
    int n,in[N],out[N],m;
    double dp[N],p[N];
    vector < pair<int,ll>  > g[N];
    queue<int>q;
    bool vis[N];
    int main(){
    	scanf("%d%d",&n,&m);
    	ll z;
    	for(int i=1,x,y;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		g[x].push_back(make_pair(y,z));
    		in[y]++;out[x]++;
    	}
    	for(int i=1;i<=n;i++)
    		if(!in[i])q.push(i);
    	vis[1]=1;p[1]=1;
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		for(int i=0;i<g[u].size();i++){
    			int v=g[u][i].first,w=g[u][i].second;
                if(!vis[v]){
                    dp[v]+=(dp[u]+w*p[u])/(double)out[u];
                    p[v]+=p[u]/(double)out[u];
                    if(--in[v]==0){
    			    	vis[v]=1;q.push(v);
    		    	}
                }
    		}
    	}
    	printf("%.2lf
    ",dp[n]);
    	return 0;
    }
    

    法二:倒着跑,in是反图的出边

    trick多起点一终点的推荐反着跑
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    #define N 100005
    int n,in[N],c[N],m;
    double dp[N];
    vector < pair<int,int>  > g[N];
    queue<int>q;
    bool vis[N];
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y,z;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		g[y].push_back(pair<int,int>(x,z));
    		in[x]++;c[x]++;
    	}
    	for(int i=1;i<=n;i++){
    		dp[i]=0;
    		if(!in[i])q.push(i);
    	}
    	vis[1]=1;
    	while(!q.empty()){
    		int u=q.front();q.pop();
    		for(int i=0;i<g[u].size();i++){
    			int v=g[u][i].first,w=g[u][i].second;
    			dp[v]+=dp[u]+w,vis[v]=1;
    			in[v]--;
    			if(in[v]==0){
    				dp[v]/=c[v];q.push(v);
    			}
    		}
    	}
    	printf("%.2lf
    ",dp[1]);
    	return 0;
    }
    

    例6:聪聪与可可

    • 猫先走一步,再走一步;(猫比老鼠先走)

    • 老鼠可以不动;

    • 猫必须走到离老鼠最近的点,如距离有相同,则选编号最小的点。

    • 预处理:

      • 由于需要知道猫走一步后的点,所以要预处理。预处理出猫在点i,老鼠在点j,猫的下一个走位 c_nxt$ [i][j] $

      • 然而预处理出这个.,就还需要使用SPFA预处理出猫在点i到达所有点最短路径 $ dis[i][j] $

        额这里边权是1 SPFA不会重新进队,所以 复杂度大概 $ O(N^2) $????????

    • 考虑使用概率DP,用$ f[i][j] $ 表示猫在点i,老鼠在点j,猫抓到老鼠的期望步数是多少。

      我们进行分类讨论:

      • 如果猫和老鼠同点,即 i=j ,则 $ f[i][j]=0 $
      • 如果猫走一步或两步可以到达 j ,$ f[i][j]=1 $
      • 否则$ f[i][j]=sum(f[sec][k]/(du[j]+1))$ (其中,sec表示猫走两步所到达的位置)
    • 这个过程可以使用记忆化搜索完成。

    • 最终答案是$ f[s][t] $

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    const int N=1010;
    const int inf=100000000;
    double p,ans;
    int n,m,cc,kk,du[N],w;
    int hd[N<<1],to[N<<1],nxt[N<<1],tot;
    inline void add(int x,int y){
    	to[++tot]=y,nxt[tot]=hd[x];hd[x]=tot;
    }
    
    int dis[N][N],c_nxt[N][N];
    bool vis[N];
    queue<int>q;
    void spfa(int s,int *dis){
    	dis[s]=0;
    	q.push(s);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    //		if(vis[x])continue;
    		vis[x]=0;
    		for(int i=hd[x];i;i=nxt[i]){
    			int y=to[i];
    			if(dis[y]>dis[x]+1){
    				dis[y]=dis[x]+1;
    				if(!vis[y]){
    					q.push(y);vis[y]=1;
    				}
    			}
    		}
    	}
    }
    
    bool visit[N][N];
    double f[N][N];
    
    double dfs(int u,int v){
    	if(visit[u][v]) return f[u][v];
    	if(u==v) return 0;
    	int fir=c_nxt[u][v];
    	int sec=c_nxt[fir][v];
    	if(fir==v||sec==v) return 1;
    	f[u][v]=1;
    	f[u][v]+=dfs(sec,v)/(du[v]+1);
    	for(int i=hd[v];i;i=nxt[i]){
    		f[u][v]+=dfs(sec,to[i])/(du[v]+1);
    	}
    	visit[u][v]=1;
    	return f[u][v];
    }
    
    int main(){	
        n=read();m=read();cc=read();kk=read();
    	for(int i=1,x,y;i<=m;i++){
    		x=read();y=read();
    		du[x]++;du[y]++;
    		add(x,y);add(y,x);
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			dis[i][j]=c_nxt[i][j]=inf;
    	for(int i=1;i<=n;i++)
    		spfa(i,dis[i]);
    	for(int x=1;x<=n;x++)
    		for(int i=hd[x];i;i=nxt[i]){
    			int y=to[i];
    			for(int j=1;j<=n;j++)
    				if(dis[x][j]==dis[y][j]+1)
    					c_nxt[x][j]=min(c_nxt[x][j],y);
    		}
    	printf("%.3lf",dfs(cc,kk));
    	return 0;
    }
    

    例7:康娜的线段树

    p[ i ]记录每个点的概率

    [1+1/2+1/4+...+1/dep ]

    然后用个sum[ ]维护 p[ ]的前缀和

    区间加就 (sum[r] - sum[l-1] )*add

    #include <iostream>
    #include <cstdio>
    #include <cctype>
    using namespace std;
    const int MAXN=1e6+5;
    int n,m,qwq,depth[MAXN],x[MAXN];
    long double p[MAXN],sum[MAXN],ans;
    inline int read() {
    	int x=0;int f=1;char ch=getchar();
    	while(!isdigit(ch)){ch=='-'&&(f=-1);ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return x*f;
    }
    void dfs(int l,int r,int dep) {
    	if(l==r) {
    		depth[l]=dep;
    		return;
    	}
    	int mid=(l+r)>>1;
    	dfs(l,mid,dep+1);
    	dfs(mid+1,r,dep+1);
    }	
    int main() {
    	n=read(),m=read(),qwq=read();
    	for(int i=1;i<=n;i++)
    		x[i]=read();
    	dfs(1,n,1);
    	for(int i=1;i<=n;i++) {
    		p[i]=2-(long double)(1.0/(1<<(depth[i]-1)));
            printf("%Lf ",p[i]);
    		ans+=x[i]*p[i];
    		sum[i]=sum[i-1]+p[i];
    	}
    	for(int i=1;i<=m;i++) {
    		int l=read(),r=read(),add=read();
    		ans+=(sum[r]-sum[l-1])*add;
    		printf("%lld
    ",(long long)(ans*qwq));
    	}   
    	return 0;
    }
    

    例8:收集邮票

    Solution

    #include <cstdio>
    #include <iostream>
    using namespace std;
    int n;
    double ans[10005],num[10005];
    int main() {
    	scanf("%d",&n);
    	num[n]=0;ans[n]=0;
    	for(int i=n-1;i>=0;i--)  {
    		num[i]=num[i+1]+(1.0*n)/(1.0*(n-i));
    		ans[i]=ans[i+1]+num[i+1]+(1.0*n/(1.0*(n-i)))+num[i]*(1.0*i/(1.0*(n-i)));
    	}
    	printf("%.2lf",ans[0]);
    	return 0;
    }
    
    

    例9:礼物(gift)

    Description

    见7.26模拟赛

    Solution

    最大喜悦值一定就是所有物品喜悦值之和。

    现在问题 转化成:n种物品,每次按一定概率拿一件物品,问拿齐所有种 类物品的期望次数是多少。

    观察到N 比较小,可以考虑状压DP。设二进制状态S 表示 当前有哪些物品已经拿了

    转移:

    $ f_s=sum{f_{s’}}$ * (p[i]+ (1-sum{p[i]})) * $f_s+1 $

    进一步化简 $ sum{p[i]}f_s=sum{f_{s’}}p[i]+1 $

    最终状态 $ f[(1<<n)-1] $

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int N=21;
    double p[N],f[1<<N];
    int n,w[N],cnt[1<<N];
    long long ans;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%lf%d",&p[i],&w[i]);
    		ans+=w[i];
    	}
    	printf("%lld
    ",ans);
    	f[0]=0;
    	for(int s=1;s<(1<<n);s++)
    		cnt[s]=cnt[s&(s-1)]+1;
    	for(int s=1;s<(1<<n);s++){
    		double sump=0;
    		for(int i=1;i<=n;i++){
    			if((s>>(i-1)&1))
    				f[s]+=p[i]*f[s^(1<<(i-1))],sump+=p[i];
    		}
    		f[s]=(f[s]+1)/sump;
    	}
    	printf("%.3lf",f[(1<<n)-1]);
    	return 0;
    }
    
  • 相关阅读:
    NGUI UIEventListener
    Unity3d NGUI Panel 滑动菜单
    NGUI 密码输入框
    Unity3d 时间差
    Unity3d 添加组件脚本和建菜单
    c# [HideInInspector] 属性
    c# [System.Serializable]
    Activity 生命周期
    Unity3d OnApplicationPause与OnApplicationFocus
    C# 中 Struct 与 Class 的区别
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13531937.html
Copyright © 2020-2023  润新知