bzoj2337 luogu3211 [HNOI2011]XOR和路径

本文介绍了一种使用概率期望动态规划解决特定类型问题的方法。通过枚举位数,定义$f_i$为从点$i$出发到点$n$,某位为1的概率,巧妙地将复杂的运算转换为概率计算。文章提供了详细的代码实现,并强调了概率期望DP中倒序推导的重要性。

https://www.lydsy.com/JudgeOnline/problem.php?id=2337

概率期望dp

若直接求期望,和与异或的运算不好转换

于是考虑枚举位数,$f_i$表示改位为从点$i$出发走到点$n$,该位为1的概率

转移还是很简单的

注意概率期望dp很多都要倒着推,一开始顺着推居然水过了样例,然后一提交就全WA

 

#include<bits/stdc++.h>
using namespace std;
const int N=105,M=1e4+5;
double eps=1e-5;
int n,m;
int deg[N],head[N],ver[M<<1],nxt[M<<1],val[M<<1],ce;
double mat[N][N],f[N],ans;
void Gauss(int now)
{
    //cout<<now<<endl;
    if(abs(mat[now][now])<eps)
    {
        for(int i=now+1;i<n;++i)
            if(abs(mat[i][now])>=eps)
            {
                for(int j=1;j<=n+1;++j) swap(mat[i][j],mat[now][j]);
                break;
            }
    }
    if(abs(mat[now][now])>=eps)
    {
        for(int i=now+1;i<n;++i)
            {
                double tmp=mat[i][now]/mat[now][now];
                for(int j=1;j<=n+1;++j) mat[i][j]-=mat[now][j]*tmp;
            }
    }
    if(now<n) Gauss(now+1);
    if(now==n) f[now]=0;
    else f[now]=mat[now][n+1]/mat[now][now];
    for(int i=now-1;i>=1;--i) mat[i][n+1]-=mat[i][now]*f[now];
}
void adde(int a,int b,int c)
{
    ver[++ce]=b,val[ce]=c,nxt[ce]=head[a],head[a]=ce;
}
int main()
{
    //freopen("1.in","r",stdin); freopen("1.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        adde(u,v,w);
        if(v!=u) adde(v,u,w);
        ++deg[v];
        if(v!=u) ++deg[u];
    }
    for(int s=0;s<=30;++s)
    {
        for(int i=1;i<=n+1;++i)
            for(int j=1;j<=n+1;++j)
                mat[i][j]=0;
        for(int i=1;i<n;++i)
        {
            mat[i][i]=-1.0;
            for(int j=head[i];j;j=nxt[j])
            {
                int y=ver[j];
                if((val[j]&(1<<s)))
                    mat[i][y]-=1.0/(double)deg[i],mat[i][n+1]-=1.0/(double)deg[i];
                else mat[i][y]+=1.0/(double)deg[i];
            }
        }
    /*    for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n+1;++j)printf("%.3lf ",mat[i][j]);
            cout<<endl;
        }*/
        Gauss(1);
        //cout<<f[1]<<endl;
        ans+=f[1]*(double)(1<<s);
    }
    printf("%.3lf\n",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/w19567/p/11317929.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值