BZOJ 3036 浅谈逆拓扑序递推期望转移方程

BZOJ 3036 浅谈逆拓扑序递推期望转移方程_第1张图片
世界真的很大
期望这个东西逐渐地也没有那么玄学了
期望的线性性决定了其可以通过递推来得到状态转移方程
有向无环图的话一定要想到拓扑序
只要有向就行不一定非要用题目给出的方向,思路还是要灵活

看题先:

description

随着新版百度空间的下线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿。

给出一个有向无环的连通图,起点为1终点为N,每条边都有一个长度。绿豆蛙从起点出发,走向终点。
到达每一个顶点时,如果有K条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 1/K 。
现在绿豆蛙想知道,从起点走到终点的所经过的路径总长度期望是多少?

input

第一行: 两个整数 N M,代表图中有N个点、M条边
第二行到第 1+M 行: 每行3个整数 a b c,代表从a到b有一条长度为c的有向边

output

从起点到终点路径总长度的期望值,四舍五入保留两位小数。

求走到终点的路径期望值
先考虑在每个点,青蛙都是随机走的,对于每个点,他走每一条出边的概率都相同
这一点在提示我们可以采用期望逐步转移的方式来得到答案
因为转移期望的必须条件之一,每一部之间的概率,我们都可以得到,就是1 / 点的度数

考虑逐步转移,由于期望具有线性性,决定了一个状态的期望可以由后继状态的期望乘以概率累加得到
令fi表示走到i点的期望步数
题目性质决定了已经到过的点不可能走回来,所以不用考虑到几次的问题
那么很显然fi= sigma (fj + w(i,j))/i的度数
因为期望线性性,所以可以由后继状态的期望值得到

考虑一个点的答案是由后继点的答案得到的,而且我们要的是第一个点的答案,而n号点的期望值肯定是0
很显然我们应该从后往前推导
所以边干脆就建成反边
为了在一张图里面倒推,而且还是DAG,很容易想到拓扑序
就倒着用拓扑序逆推就好了

完整代码:

#include
#include
#include
using namespace std;

struct edge
{
    int u,v,w,last;
}ed[500010];

queue <int> state;

int n,m,num=0,in[200010],du[200010],head[200010];
double f[200010];

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

void TOP()
{
    state.push(n);
    f[n]=0;
    while(!state.empty())
    {
        int u=state.front();
        state.pop();
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            in[v]--,f[v]+=(double) (ed[i].w+f[u])/du[v];
            if(!in[v])
                state.push(v);
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        add(v,u,w);
        in[u]++,du[u]++;
    }
    TOP();
    printf("%0.2lf\n",f[1]);
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

你可能感兴趣的:(期望DP)