「动态规划」第1章 期望概率 DP
有关期望的知识见 概率论总结.
例题
A. 【例题1】期望分数
维护 \(\sum X^3\) 不好做,但是注意到可以拆成 \(\sum X^2\) 的递推形式,进一步拆成 \(\sum X\) 的递推形式,而后者很好维护。算是一个 trick 吧。
B. 【例题2】选书问题
注意到每个人选择某本书的概率可以独立算出,因此我们先算出这部分。剩下的部分,其实就是求每对逆序对的概率,直接用树状数组就做完了。
C. 【例题3】乘坐电梯
感觉就是普通的推式子,不知道为什么会有 \(O(n^2)\) 的 DP。
根据全期望公式,容易发现答案为 \(\sum\limits_{i=1}^t\min(i,n)\binom{t}{i}p^i(1-p)^{t-i}\)。这里 之所以要枚举 \(n+1\le i\le t\) 的部分是因为所有人上完之后,剩下的时间必定不上人,我们当成“无论成不成功都不产生贡献”处理(也就是 \(\min(i,n)\) 的贡献)。
教训:double 输出时不一定会把所有位数都输全,所以要写 setprecision 才能保证输出的精度正确。
D. 【例题4】图上游走
非常板的一道题目。印象中htc提出了一个非常深刻的问题,但是我忘了。
E. 彩色圆环
首先有一种稚嫩的 DP:\(dp_{i,0/1}\) 表示考虑前 \(i\) 个位置,最后一个位置与第一个位置不同/相同的期望答案。但是这样的话,由于是一个环,结尾和开头颜色可能相同,无法计算这一部分贡献。(你可能想到强制结尾与开头不相同,并乘上真实的开头在第一段中的哪一个位置,但是由于不知道第一段的长度所以也无法做)
一种成熟的 DP:枚举第一段的长度,并把第一段缩成一个点(即第一个点)。\(dp_{i,0/1}\) 表示考虑前 \(i\) 个位置,前两个位置颜色不同,最后一个位置与第一个位置不同/相同的期望答案。这么做就知道第一段有多长了,就可以算了。最后,记得考虑整个环颜色一样的情况,这种情况无法被 DP 处理。
他人的一些深刻的认识:https://www.cnblogs.com/sun123zxy/p/12245594.html
代码存档
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=200+10;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll n,m;
double s[N],dp[N][2];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
s[1]=1;
for(ll i=2;i<=n;i++)
s[i]=s[i-1]/m;
dp[1][1]=1;
for(ll i=2;i<=n;i++)
{
for(ll j=1;j<i;j++)
{
dp[i][0]+=(dp[i-j][0]*(m-2)/m+dp[i-j][1]*(m-1)/m)*j*s[j];
dp[i][1]+=dp[i-j][0]/m*j*s[j];
}
}
// for(ll i=1;i<=n;i++)
// cout<<dp[i][0]<<" "<<dp[i][1]<<endl;
double ans=n*s[n];
for(ll i=1;i<n;i++)
{
ans+=dp[n-i+1][0]*i*i*s[i];
}
cout<<fixed<<setprecision(5)<<ans<<endl;
return 0;
}
F. 关灯游戏
引理:若一个灯只能影响到比它小的灯,且必定影响它自己,则关灯方案的奇偶性唯一。
这可以由线性代数知识证明:由于上述情况列成方程构成一个上三角矩阵,而上三角矩阵一定满秩,因此有唯一解。
我们只需要贪心地从大往小,需要按开关则按,就可以得出每个灯被按的次数的奇偶性了。
接下来是一个典中典的“进步型”期望DP。\(dp_{i}\) 表示接下来还要按灭几个灯,如果按对了则从 \(dp_{i-1}\) 转移,按错了从 \(dp_{i+1}\) 转移。推推式子就行了。
G. 守卫挑战
这是一道非常暴力的背包概率题。\(dp_{i,j,k}\) 表示考虑前 \(i\) 次挑战,已经胜利了 \(j\) 次,背包剩余容量为 \(k\) 的概率。注意到 \(k\) 可能为负数,也可能非常大,所以加上一个偏移量,而上限设置为 \(2n\) 即可,因为达到 \(2n\) 之后绝无可能空间不够了。剩下的细节就不说了。

浙公网安备 33010602011771号