期望dp几种常见设转移方程数组的方法
1、设f [ i ]表示的是由i 状态变成 最终状态的期望 (由末状态逆推)
2、按照题意直接设
3、把选择的东西加入数组,如f [ i ] [ j ]表示第i个物品选j个的期望
或f [ i ] [ j ]表示有i个A 物品,j个B物品的期望
(结合第一种的话就是,dp[i]j[j]:已经有i个A ,j个B 离达到最终状态还差多少期望)
求转移方程
先考虑逆向。(从最终状态的解开始逆推)
如果逆向没有思路,则考虑正向。
(一般而言,末状态已经决定了的话,就是逆向递推了)
也可以先看看边界在哪边,从边界开始递推转移。
题意:给定一个n面的骰子,问投出n个不同的面的期望投掷次数。 (1<=n<=1000)
分析:先考虑逆向转移,dp[n]=0 表示投出n个不同面之后,要达到投出n个不同面的状态还需要投掷0次。
即:dp[i]表示已经投出了i个面,要投出剩余n-i面的期望次数。
对于当前状态为i,投一次骰子,有i/n的可能投中已经出现的i个面之一,此情况下还需要投dp[i]次
有(n-i)/n的可能投出其余n-i面。此情况下还要投dp[i+1]次
即:由于投出的面可能出现过,也可能没出现过,所以dp[i]由dp[i] 与 dp[i+1] 转移而来
dp[i]=dp[i]*(i/n) + dp[i+1]*((n-i)/n) +1 (+1是因为要投一次骰子才能转移)
移项变成:dp[i]=dp[i+1] + n/(n-i)
#include
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e3+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//给定n面的骰子 求投的期望次数 满足每面都至少出现一次
double dp[maxn];//dp[i] 表示已经出现i个面时 投出剩余n-i面的期望次数 dp[n]=0
//再投一次 i/n的几率投中已经有的面,此情况下还需要投dp[i]次骰子,(n-i)/n几率投中新的面 此情况下还要投dp[i+1]次
//dp[i]=(i/n)dp[i] + (n-i)/n *dp[i+1] +1
//(n-i)/n * dp[i]= (n-i)/n *dp[i+1] +1
//dp[i]=dp[i+1] + n/(n-i)
int main()
{
int t;scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
dp[n]=0.0;
for(int i=n-1;i>=0;--i)
{
dp[i]=dp[i+1] + 1.0*n/(n-i) ;
}
printf("%.2f\n",dp[0]);
}
return 0;
}
也可以顺推,dp[i]表示投出i个面的期望次数,
dp[i] = dp[i-1] + n/(n-(i-1)) , 也就是投出i-1面的期望加上投一个新面的期望。
#include
#include
#include
using namespace std;
double dp[1005];
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;i++)
dp[i]=dp[i-1]+(double)n/(double)(n+1-i);
printf("%.2f\n",dp[n]);
}
return 0;
}
题意:n个空白格子,随机选一个涂色,一共涂m次,问最终被涂色格子数目的期望。
(1<=n,m<=1e5)
分析:这题考虑逆向的话行不通。
而第i次涂色可由第i-1次转移而来,所以正向转移特别好想。
dp[i]表示涂了i次 被涂色格子数目期望
dp[i]由dp[i-1]转移而来,第i次涂色: dp[i-1]/n的概率涂中已经被涂色的格子,这种情况下答案仍是dp[i-1]
(n-dp[i-1])/n的概率涂中空白格子,这种情况下答案是dp[i-1]+1
dp[i]=(dp[i-1]/n) *dp[i-1] + (n-dp[i-1])/n * (dp[i-1]+1)
细节是这题误差要求1e-9,所以记得保留9位小数点输出
#include
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e5+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//n个格子 每次随机给一个染色 问m次染色后被涂上色的格子期望个数
//dp[i] i次染色后 涂色格子的期望个数 dp[1]=1
//第i次染色 (dp[i-1]/n)的几率涂中已经有颜色的格子,这种情况下仍然是dp[i-1] , (n-dp[i-1])/n 的几率涂中新格子
//dp[i]=(dp[i-1]/n)*dp[i-1] + (n-dp[i-1])/n *(dp[i-1]+1)
int n,m;
double dp[maxn];
int main()
{
int t;t=1;
while(t--)
{
scanf("%d %d",&n,&m);
dp[1]=1.0;
double num=n*1.0;
for(int i=2;i<=m;i++)
{
dp[i]=(dp[i-1]/num)*dp[i-1] + (num-dp[i-1])/num * (dp[i-1]+1);
}
printf("%.9f\n",dp[m]);
}
return 0;
}
题意:投一个硬币,第n次投的代价是2*n-1, 投中正面朝上的概率为给定的p,
投到k次正面朝上就停止 。 问投掷次数的期望,代价的期望。
分析:这题同样从正向开始考虑
E[i]表示投到i次正面朝上的次数期望, 当前投掷有p的概率是正面,1-p的概率是反面
E[i] = p*(E[i-1]) + (1-p)*(E[i]) + 1
化简一下就是E[i]=1/p+E[i-1] ,即:E[i]=i/p
(也可以这样理解,1次正面朝上的期望是1/p,每次投掷都是独立事件,所以i次正面朝上的期望次数就是i*1/p)
f[i]表示i次正面朝上的代价期望。
当前投掷有p概率是正面,这种情况下 当前投掷是第dp[i-1]+1次投掷,代价是2*(E[i-1]+1)-1
有1-p的概率是反面,此时的投掷是第dp[i]+1次投掷,代价是2*(E[i]+1)-1
f[i]=p*(f[i-1]+2*(E[i-1]+1)-1) + (1-p)*(f[i]+2*(E[i]+1)-1)
化简一下就是:f[i]=(f[i-1]+2*(E[i-1]+1)-1) +(1-p)/p * ( 2*(E[i]+1)-1 )
#include
#include
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e3+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//每天丢硬币 p的概率正面 出现k次正面就停止丢硬币
//double dp[maxn];
//出现i次正面 投掷次数期望
//dp[i]=p*dp[i-1] + (1-p)*dp[i] +1 <==> dp[i]=dp[i-1]+1/p dp[i]=k/p
double ans1;
double p;
int k;
double f[maxn];//i次正面向上的花费
double E[maxn];
//f[1]=k/p
//f[i]=p*(f[i-1]+2*(dp[i-1]+1)-1) + (1-p)*(f[i]+2*(dp[i]+1)-1)
//p*f[i]=p*(f[i-1]+2*(dp[i-1]+1)-1) + (1-p)*( 2*(dp[i]+1)-1 )
//f[i]=(f[i-1]+2*(dp[i-1]+1)-1) +(1-p)/p * ( 2*(dp[i]+1)-1 )
//如果抛出正面 当前这个是第dp[i-1]+1次投掷 等差数列中的第dp[i-1]+1项
//如果抛出反面 当前这个就是dp[i]+1次投掷
int main()
{
// int t;scanf("%d",&t);
while(~scanf("%d",&k) && k)
{
scanf("%lf",&p);
f[0]=0,E[0]=0;
for(int i=1;i<=k;i++)
{
E[i]=1/p+E[i-1];
f[i]=(f[i-1]+2*(E[i-1]+1)-1) +(1-p)/p * ( 2*(E[i]+1)-1 );
}
printf("%.3f %.3f\n",E[k],f[k]);
}
return 0;
}
题意:一共有s个系统 ,共有n种bug, 每天可以找到一个bug(发生在每个系统的概率为1/s, 每种bug出现的概率为1/n)。 问每个系统都至少找到1个bug 且每种bug都被发现的期望天数 。
分析:这题和第一题投骰子,每面至少出现一次的思路是一样的,都是逆推。
dp[i][j]:在i个系统中出现了j种bug的状态 要到达 在s个系统中出现n种bug的剩余期望天数。
dp[s][n]=0;
每天找到的那个bug有4种情况,分别考虑进去就可以了。
#include
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 1e3+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
//s个系统 共有n种bug 每天找到一个bug(每个系统1/s 每种bug 1/n) 问每个系统都找到bug 且每种bug都被发现的期望天数
//这题和投骰子每个面都出现的dp方法类似
//dp[i][j] 出现了i个系统 j种bug的状态 要到达出现s个系统 n种bug的剩余期望天数
//dp[s][n]=0
//dp[i][j]=i/s*j/n *dp[i][j] (没有出现在新系统 不是新bug)
//+(s-i)/s*j/n * dp[i+1][j] (出现在新系统 不是新bug)
//+(n-j)/j* i/s * dp[i][j+1] (没有出现在新系统 是新bug)
//+(s-i)/s * (n-j) /n *dp[i+1][j+1] (出现在新系统 是新bug)
// + 1 bug出现当天的天数
int n,s;
double dp[maxn][maxn];
int main()
{
while(~scanf("%d %d",&n,&s))
{
dp[s][n]=0;
for(int i=s;i>=0;i--)
{
for(int j=n;j>=0;j--)
{
if(i==s && j==n) continue;
dp[i][j]=1.0;
dp[i][j]+=(1.0*s-i)*j / (1.0*s*n) *dp[i+1][j];
dp[i][j]+=(1.0*n-j)*i / (1.0*n*s) *dp[i][j+1];
dp[i][j]+=(1.0*s-i)*(1.0*n-j)/ (1.0*s*n) * dp[i+1][j+1];
dp[i][j]/=(1.0-1.0*(i*j)/(s*n));
}
}
printf("%.4f\n",dp[0][0]);
}
return 0;
}
题意:给定一段连续区间的格子[0,n],从0出发,每次投骰子,1~6等概率,投中i前进i格,另有m个格子标了数x[i] 到了这个格子可以立刻传送到第x[i]格,x[i]>i 。 问到达或超过第n格的期望投掷次数。 分析:对于这种最终状态已经决定了的问题,基本就确定是逆向递推了。 题意:给定带边权的无向图,默认了一条路径c[1] c[2]…c[n] ,路径每个点又有一个备用点d[i], 可以选定不超过m个点,让c[i]以p[i]的概率变成d[i] , 问如何选择可以使得最终路径的期望最短路最小 。 分析:先用floyd跑出最短路。 题意:n个人排队进电梯 , 每一秒队首有p概率进电梯 , 一次只能进一个人, 且只能按排队顺序进 问t秒后电梯里的人数期望。 分析:看数据大概能猜出dp有两个维度,一个是时间,一个是人数。 刚开始我打算设dp[i][j]为:经过t秒,第j个人成为队首的状态下 ,电梯人数的期望 。但是很显然这是个假的状态,(无法转移+遗漏了解空间) 。 正解: 参考大佬博客CLICK IT!
(1dp[i]表示已经到达i,要到达最终状态(n或超过n)的期望次数。
dp[n]=dp[n+1]...=dp[n+6]=0;
当i格子没有标x[i],dp[i]=(dp[i+1]+d[i+2]...+dp[i+6])/6 +1;
否则 dp[i]=dp[x[i]]
ans=dp[0]
#include
P1850 [NOIP2016 提高组] 换教室
(图的顶点<=300)
dp[i][j][0/1]: 走到路径第i个点,已经选了j个点变成d[i],第i个点没选/选 状态下的最优解。
边界dp[1][1][1]=dp[1][0][0]=0 ,然后j=0的边界也都先处理一下。#include
Ilya and Escalator CodeForces - 518D
(1<=n,t<=2e3)dp[i][j]表示经过了i秒,电梯里有j个人的概率 。
最终答案就是Σdp[t][i]*i
边界:dp[i][0]=(1.0-p)^i
转移:dp[i][j]=dp[i-1[j]*(1.0-p) + dp[i-1][j-1]*p
(dp[i-1][j]状态下不进人的概率*dp[i-1][j] + dp[i-1][j-1]状态下进人的概率*dp[i-1][j-1])
(值得注意的是,当j==n, dp[i-1][j]这个状态下电梯再进人的概率为0,所以要判断)
#include
P1654 OSU!
#include