数学期望 E(x)=Σ每一种状态*对应的概率
状态很多,有时候不能完全枚举.
大多数题都是找公式,规律,动态规划计算,数学期望的dp一般要逆推,处理好边界
一般的dp是到dp[x]状态有多少,答案为dp[n]
而数学期望的dp[x]已经在状态x,距离n还差多少,答案为dp[0].
学习:https://www.cnblogs.com/kuangbin/archive/2012/10/02/2710606.html
题集:
1.Favorite Dice:https://vjudge.net/problem/SPOJ-FAVDICE
题意:
给一个n面的筛子,求每个面都朝上过至少1次,需要投几次的期望
解析:
期望dp
期望dp一般逆向推导
dp[i]表示还差i面的期望,这个时候已经出了n-i面
dp[n]=0,开始就差n面
还差i面的时候,投一次,无效出现的情况为(n-i)/n,出现有效的情况为i/n,不论是否有效,次数都增加了一次
dp[i]=(i/n)*dp[i]+((n-i)/n)*dp[i]+1
ac:
#include <bits/stdc++.h>
using namespace std;
double dp[1100];
int main()
{
int t,n;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
dp[n]=0;
for(int i=n-1;i>=0;i--)
{
dp[i]=dp[i+1]+n*1.000000/(n-i);
}
printf("%lf\n",dp[0]);
}
return 0;
}
题意:
给定n个门,通过每个门需要花费一定的时间,时间为a[i]的绝对值,如果a[i]为负数,呢么通过门后会传送会原地,否则会传送走
每次等概率的选择一扇门,求传送走花费时间的期望
解析:
传送走的方法无穷种,没办法列出期望公式
1.直接传送走,期望为1/n*t
2.传送回来,期望为1/n*t+e
sum1传送走的时间和,sum2传送回来的时间和,door2传送回来的门数目
e=1/n*(sum1)+1/n*(sum2+door2*e)
化简得:e=(sum1+sum2)/(n-door2)
ac:
#include<bits/stdc++.h>
#define MAXN 105
using namespace std;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int x;
int main()
{
int t,n,cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int m=0,ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
if(x<0)
m++,ans+=-x;
else ans+=x;
}
int k=n-m;
if(k==0)
printf("Case %d: inf\n",cas++);
else{
int kk=gcd(k,ans);
printf("Case %d: %d/%d\n",cas++,ans/kk,k/kk);
}
}
return 0;
}
https://vjudge.net/problem/UVA-10900
题意:
题意:一共可以答n道题目,初始奖金为1,每次答对题目奖金翻倍,答错奖金清零。每次答对题目的概率在[t,1]均匀分布.求最佳策略下能获得的期望奖金.
解析:
定义dp[i]为答对i题后获得的钱的最大期望
如果挑战成功的概率*下一轮的奖励期望>=当前轮的最大期望 就选择挑战
我们从后往前推
定义p为一个临界概率,p*dp[i+1]=dp[i]=>p=dp[i]/dp[i+1]=>(1<<i)/dp[i+1];
如果t>p,就一定选择挑战
如果t<p,概率在(t-p)/(1-t)的时候选择不挑战,概率(1-p)/(1-t)的时候挑战,挑战了也不一定成功,还要乘成功的概率(p+1)/2
if(t-p>=0)
dp[i]=(1+t)/2*dp[i+1];
else
dp[i]=(p-t)/(1-t)*bit[i]+(1-p)/(1-t)*(p+1)/2*dp[i+1];
ac:
#include<bits/stdc++.h>
using namespace std;
int bit[35];
double dp[35];
void init()
{
bit[0]=1;
for(int i=1;i<=33;i++)
bit[i]=bit[i-1]*2;
}
int main()
{
init();
int n;
double t;
while(scanf("%d %lf",&n,&t)!=EOF)
{
if(n==0&&fabs(t-0)<=0.000001)
break;
dp[n]=bit[n];
for(int i=n-1;i>=0;i--)
{
double p=bit[i]/dp[i+1];
if(t-p>=0)
dp[i]=(1+t)/2*dp[i+1];
else
dp[i]=(p-t)/(1-t)*bit[i]+(1-p)/(1-t)*(p+1)/2*dp[i+1];
}
printf("%.3f\n",dp[0]);
}
return 0;
}
https://blog.youkuaiyun.com/Izumi_Hanako/article/details/100186142
题意:
有一个机器人从1走到n,他等概率的花费一定耐久度选择走下一步中的任意一步或者静止不动,耐久度为机器人从出发开始经过的天数.
解析:
设e[u]为从u走到n经过的天数的期望,dp[u]为从u走到n期望的耐久度.
e[u]=∑(e(v)/(out(v)+1) +(e(u)/out(u)+1)+1,分别为从v过来,原地静止1次,到达后本来就停1天
dp[u]=∑(f(v)/(out(v)+1) +(f(u)/out(u)+1)+e(u),和上面的式子非常相像,只是将1天换乘e(u)
同拓扑排序从后往前求:
ac:
#include<bits/stdc++.h>
#define MAXN 200005
#define ll long long
using namespace std;
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1];
int in[MAXN<<1],out[MAXN];
vector<int> vc[MAXN];
double dp[MAXN<<1];
double e[MAXN<<1];
int tot,n;
void init()
{
tot=0;
for(int i=0;i<MAXN;i++)
head[i]=in[i]=out[i]=dp[i]=e[i]=0,vc[i].clear();
}
void add(int u,int v)
{
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void get_e(int u)
{
if(u==n)
return ;
double tmp=0;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
tmp+=e[v];
}
e[u]=(tmp+out[u]+1)/out[u];
}
void get_dp(int u)
{
if(u==n)
return ;
double tmp=0;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
tmp+=dp[v]+e[u];
cout<<u<<" "<<v<<endl;
}
dp[u]=(tmp+e[u])/out[u];
}
void topo()
{
queue<int> que;
que.push(n);
while(que.size())
{
int u=que.front();
get_e(u);
get_dp(u);
que.pop();
for(int i=0;i<vc[u].size();i++)
{
int v=vc[u][i];
in[v]--;
if(in[v]==0)
que.push(v);
}
}
printf("%.2f\n",dp[1]);
printf("%.2f\n",e[1]);
}
int main()
{
int t,m,u,v;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
vc[v].push_back(u);
in[u]++;
out[u]++;
}
topo();
}
return 0;
}