标签:概率 期望 int 笔记 times read maxn dp deg
概率期望
省流:期望 \(=\) 概率 \(\times\) 权值
好,开始看题。
1.P4316 绿豆蛙的归宿
求在 \(\text{DAG}\) 上所走边权的期望,显然需要拓扑排序,有正推和逆退两种方式。
设 \(G\) 表示节点 \(u\) 直接相连节点的集合。
逆推
比较常用,需要建反图。
不难发现对于点 \(u\) 的期望,有:
\[E(u)=\sum_{v\in G}\left(\frac{1}{deg(u)}\times (E(v)+w)\right) \]发现 \(deg(u)\) 恰好等于 \(G\) 的元素个数,因此上式等价于:
\[E(u)=w+\sum_{v\in G} \left(\frac{1}{deg(u)}\times E(v) \right) \]int deg[maxn],_in[maxn];
queue<int> q;
db f[maxn];
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add_edge(v,u,w);
deg[u]++,_in[u]++;
}
q.push(n);
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
_in[v]--;
f[v]+=(f[u]+e[i].w)*1.0/deg[v];
if(!_in[v]) q.push(v);
}
}
printf("%.2f\n",f[1]);
return 0;
}
正推
并非建正图就能解决,这里不能使用逆推中的化简式。
依旧是一般式:
拆开来就是:
\[E(u)=\sum_{v\in G}\left(\frac{1}{deg(v)}\times E(v)\right)+\sum_{v\in G} \left(\frac{1}{deg(v)}\right)\times w \]显然右半部分不一定为 \(w\),所以要单独维护一个概率。
int deg[maxn],_in[maxn];
queue<int> q;
db f[maxn],p[maxn];
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),w=read();
add_edge(u,v,w);
deg[u]++,_in[v]++;
}
q.push(1);
p[1]=1.00;
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
_in[v]--;
f[v]+=(f[u]+e[i].w*p[u])*1.0/deg[u];
p[v]+=p[u]/deg[u]*1.0;
if(!_in[v]) q.push(v);
}
}
printf("%.2f\n",f[n]);
return 0;
}
2.P4206 NOI2005 聪聪和可可
毒瘤题。
(1)预处理
来看两者的移动方法:
- 聪聪每次移动两步,最后一次可移动一步,每一步都会选择离可可最近且标号最小的节点
- 可可等概率移动一步或留在原地
显然聪聪的移动是个贪心,可以预处理出 \(pre(u,v)\) 表示聪聪和可可分别在 \(u,v\) 两点时聪聪应去到的节点。
稀疏图用 \(n\) 次已死算法 \(\text{SPFA}\) 只要判断最短路的松弛关系就能预处理出 \(pre\) 数组。
用 \(\text{dfs}\) 会迷之爆炸。
(2)递推
这里用到 \(\text{dfs}\) 其实是记忆化,设一个节点本身和与它直接相连的节点集 \(G\),\(dp(u,v)\) 表示聪聪在节点 \(u\),可可在节点 \(v\) 时的期望,就有:
\[dp(u,v)=\sum_{v\in G} \left(\frac{1}{deg(u)+1}\times (dp(pos,v)+1)\right) \]其中 \(pos=pre(pre(u,v),v)\),特别地令 \(dp(u,u)=0\),所有对于能在两步以内到达的 \((u,v)\),令 \(dp(u,v)=1\),然后大力记忆化暴搜。
不要用 \(vector\) 存图
int pre[1005][1005],dis[1005][1005];
db dp[1005][1005];
bool vis[1005],Vis[1005][1005];
inline void spfa(int s){
memset(dis[s],0x3f,sizeof(dis[s]));
dis[s][s]=0;
vis[s]=1;
queue<int> q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[s][v]>dis[s][u]+1){
dis[s][v]=dis[s][u]+1;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
inline db dfs(int x,int y){
if(Vis[x][y]) return dp[x][y];
Vis[x][y]=1;
if(x==y) return dp[x][y]=0.0;
int u=pre[x][y],v=pre[u][y];
if(u==y||v==y) return dp[x][y]=1.0;
dp[x][y]=1.0;
for(int i=head[y];i;i=e[i].nxt){
int z=e[i].to;
dp[x][y]+=dfs(v,z)/(deg[y]+1);
}
dp[x][y]+=dfs(v,y)/(deg[y]+1);
return dp[x][y];
}
int main(){
n=read(),m=read();
st=read(),ed=read();
for(int i=1;i<=m;i++){
int u=read(),v=read();
add_edge(u,v);
add_edge(v,u);
}
for(int i=1;i<=n;i++){
spfa(i);
}
memset(pre,0x3f,sizeof(pre));
for(int x=1;x<=n;x++){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
for(int z=1;z<=n;z++){
if(dis[x][z]==dis[y][z]+1) pre[x][z]=min(pre[x][z],y);
}
}
}
printf("%.3lf\n",dfs(st,ed));
return 0;
}
3.P1654 OSU!
前置知识
\((\alpha+1)^2=\alpha^2+2\times \alpha +1\)
\((\alpha+1)^3=\alpha^3+3\times \alpha ^2+3\times \alpha +1\)
题目要求长度 \(n\) 的 \(0-1\) 串中 \(1\) 串长度的立方期望,考虑分步骤求解。
(1)前 \(i\) 位中第 \(i\) 位为 \(1\) 的线性期望
显然是由上一位得来,只考虑第 \(i\) 位是 \(1\) 的情况,那么线性期望 \(x_1\) 为:
\[x_1(i)=(x_1(i-1)+1)\times p_i \](2)前 \(i\) 位中第 \(i\) 位为 \(1\) 的平方期望
同上一问,依旧是只考虑第 \(i\) 位是 \(1\) 的情况,注意平方的期望与期望的平方存在差异,不能由第一问的结果直接得到。
\[x_2(i)=(x_2(i-1)+2\times x_1(i-1)+1)\times p_i \](3)前 \(i\) 位中第 \(i\) 位为 \(1\) 的立方期望
\[x_3(i)=(x_3(i-1)+3\times x_2(i-1)+3\times x_1(i-1)+1)\times p_i \](4)得解
第三问的结果仍不能作为答案,原因是我们只考虑最后一位是 \(1\) 的情况,答案实际为前 \(n\) 位的总和,这里需要分两部分,成功连接的概率是 \(p_i\),反之概率是 \(1-p_i\),于是有:
\[\begin{aligned} f(i)&=(f(i-1)+3\times x_2(i-1)+3\times x_1(i-1)+1)\times p_i+f(i-1)\times (1-p_i)\\ &= f(i-1)+(3\times x_2(i-1)+3\times x_1(i-1)+1)\times p_i \end{aligned}\]发现最终答案与第三问无关,可以删去直接求最后结果。
int n;
db p;
db x1[maxn],x2[maxn],x3[maxn];
int main(){
n=read();
for(int i=1;i<=n;i++){
scanf("%lf",&p);
x1[i]=(x1[i-1]+1)*p;
x2[i]=(x2[i-1]+x1[i-1]*2+1)*p;
x3[i]=x3[i-1]+(x1[i-1]*3+x2[i-1]*3+1)*p;
}
printf("%.1lf\n",x3[n]);
return 0;
}
4.P1365 WJMZBMR打osu! / Easy
同上一题,似乎更简单。
int n;
char s[maxn];
db len,ans;
int main(){
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++){
db p;
if(s[i]=='o') p=1.0;
else if(s[i]=='x') p=0.0;
else p=0.5;
ans=ans+(len*2+1)*p;
len=(len+1)*p;
}
printf("%.4lf\n",ans);
return 0;
}
标签:概率,期望,int,笔记,times,read,maxn,dp,deg 来源: https://www.cnblogs.com/SoyTony/p/15864904.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。