【BZOJ3143】游走(HNOI2013)-DP+概率期望+高斯消元

测试地址:游走
做法:本题需要用到DP+概率期望+高斯消元。
首先根据期望可加性,我们知道路径和的期望等于每条边的期望经过次数乘上边权。又根据排序不等式,我们知道给大的期望次数分配小的编号是最优的,那么现在问题就变成求每条边的期望经过次数。
我们可以先求出每个点的期望经过次数 pi p i ,然后边 (i,j) ( i , j ) 的期望经过次数就是 pideg(i)+pjdeg(j) p i d e g ( i ) + p j d e g ( j ) ,其中 deg(i) d e g ( i ) 表示点 i i 的度数。那么对于每个点我们有状态转移方程:
pi=(i,j)E1deg(j)pj
把所有点的方程列出来后, O(n3) O ( n 3 ) 高斯消元直接把未知数求出来即可。
这里要注意几个问题:一是因为点 n n 是终止节点,所以它对所有其他点都不会做出贡献,直接去掉。二是点1的方程实际上是 p1=1+(1,j)E1deg(j)pj p 1 = 1 + ∑ ( 1 , j ) ∈ E 1 d e g ( j ) p j ,因为游走是从点 1 1 <script type="math/tex" id="MathJax-Element-37">1</script>开始的,所以必定先有一次经过,然后才开始随机游走的过程。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
int n,m;
double g[510][510]={0},deg[510]={0},p[510];
struct edge
{
    int a,b;
    double p;
}e[300010];

void gauss(int n)
{
    for(int i=1;i<=n;i++)
    {
        int mx=i;
        for(int j=i;j<=n;j++)
            if (fabs(g[j][i])>fabs(g[mx][i])) mx=j;
        for(int j=i;j<=n+1;j++)
            swap(g[i][j],g[mx][j]);
        for(int j=1;j<=n;j++)
            if (j!=i)
            {
                for(int k=i+1;k<=n+1;k++)
                    g[j][k]-=g[j][i]*g[i][k]/g[i][i];
                g[j][i]=0.0;
            }
    }
}

bool cmp(edge a,edge b)
{
    return a.p>b.p;
}

void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&e[i].a,&e[i].b);
        deg[e[i].a]++,deg[e[i].b]++;
    }
    for(int i=1;i<=m;i++)
    {
        if (e[i].b!=n) g[e[i].a][e[i].b]=-1.0/deg[e[i].b];
        if (e[i].a!=n) g[e[i].b][e[i].a]=-1.0/deg[e[i].a];
    }
    for(int i=1;i<n;i++)
        g[i][i]=1.0;
    g[1][n]=1.0; 
}

void work()
{
    gauss(n-1);
    for(int i=1;i<n;i++)
        p[i]=g[i][n]/g[i][i];
    for(int i=1;i<=m;i++)
    {
        e[i].p=0;
        if (e[i].a!=n) e[i].p+=p[e[i].a]/deg[e[i].a];
        if (e[i].b!=n) e[i].p+=p[e[i].b]/deg[e[i].b];
    }
    sort(e+1,e+m+1,cmp);
    double ans=0.0;
    for(int i=1;i<=m;i++)
        ans+=e[i].p*(long double)i;
    printf("%.3lf",ans);
}

int main()
{
    init();
    work();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值