bzoj3143 洛谷3232 HNOI2013 游走 期望dp+高斯消元

题目链接
首先,我们想要求总的期望,那么我们发现如果我们能求出每条边的期望,之后对所有边的期望排序,贪心地给期望经过次数多的边赋小的编号,给期望经过次数少的边赋大的编号即可。
那么我们需要考虑如何求所有边的期望经过次数。
我们发现,每条边的期望次数是 d p x d e g x + d p y d e g y ,其中 x y 是当前边的两个端点, d p i 数组表示点 i 的期望经过次数, d e g i 表示在无向图中点 i 的度数。
我们发现 d e g 数组我们可以在读入这些边的时候顺便处理出来,那么我们只需要求出每个点期望经过的次数即可了。
那么我们考虑如何求每个点的期望经过次数。我们发现,每个点的期望经过次数是 d p v = u v u       d p u d e g u 。特别地,根据题意, n 号点经过并且只经过一次,所以 d p n = 1 。另外, 1 号点为起点,一开始就会经过一次,所以 1 号点的期望步数是 d p 1 = u 1 u       d p u d e g u + 1
我们发现,我们可以对每个点列一个方程,这样可以高斯消元解出每一个点的期望经过次数。有了每个点的期望经过次数之后,我们可以通过之前的讲解求出每条边的期望步数并贪心地给边编号,最后统计答案。
思路就是这样子,实现的时候我觉得在高斯消元时要注意一下细节,特别是对于 n 号点,它的 d p 值是 1 ,但是我们在实际操作时把第 n 行只有 d p n 的系数设为 1 ,其他的所有系数都设为 0 ,包括增广的那一列也是0。但是这样我们会发现最后一个式子成了 d p n = 0 。这样做是因为我们在考虑每个点给每条边的影响时, n 号点是不会对与它相连的边产生贡献的,因为到了 n 号点就不能再返回了,所以为了正确地统计边的期望经过次数,强制将点 n 的期望经过次数设为了 0 ,实际上应该是 1 的。而把 d p n 的系数设为 1 的原因是为了避免在做 n 个元的高斯消元时 d p ( n + 1 ) / d p n 时因为除以 0 而RE。主要还是我高斯消元写得有点少。
最后是代码:

#include <bits/stdc++.h>
using namespace std;

int n,m,hed[1000],cnt;
struct node
{
    int from,to,next;
}a[5000010];
double dp[1001],b[502][502],ans,e[5000100],deg[510];
void add(int from,int to)
{
    a[++cnt].to=to;
    a[cnt].from=from;
    a[cnt].next=hed[from];
    hed[from]=cnt;
}
void gauss()
{
    for(int i=1;i<=n;++i)
    {
        int r=i;
        for(int j=i+1;j<=n;++j)
        {
            if(abs(b[j][i])>abs(b[r][i]))
            r=j;
        }
        if(r!=i)
        swap(b[r],b[i]);
        for(int j=i+1;j<=n;++j)
        {
            double t=b[j][i]/b[i][i];
            for(int k=i;k<=n+1;++k)
            b[j][k]-=t*b[i][k];
        }
    }
    for(int i=n;i>=1;--i)
    {
        for(int j=n;j>i;--j)
        b[i][n+1]-=b[i][j]*b[j][n+1];
        b[i][n+1]/=b[i][i];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);//正向边和反向边只算一条 
        ++deg[x];
        ++deg[y];
    }
    for(int i=1;i<=n-1;++i)
    b[i][i]=-1;
    for(int i=1;i<=m;++i)
    {
        b[a[i].from][a[i].to]+=(double)1.0/deg[a[i].to];
        b[a[i].to][a[i].from]+=(double)1.0/deg[a[i].from];
    }
    for(int i=1;i<=n-1;++i)
    b[n][i]=0;
    b[n][n]=1;
    b[1][n+1]=-1;
    gauss();
    for(int i=1;i<=m;++i)
    e[i]=b[a[i].from][n+1]/deg[a[i].from]+b[a[i].to][n+1]/deg[a[i].to];
    sort(e+1,e+m+1);
    for(int i=1;i<=m;++i)
    ans+=e[i]*(m-i+1);
    printf("%.3lf\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/80630519