bzoj3143游走——期望+高斯消元

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3143

只需算出每条边被经过的概率,将概率从小到大排序,从大到小编号,就可得到最小期望;

每条边经过的概率是其两端的点被走的次数/该点的度数的和;

而每个点被走的次数又需要从与其相连的点推过来,所以构成n个n元方程,进行高斯消元求解;

其中点n较为特殊,可以不去管它,因为所有路径到n后就不再走出来,也就是n到n的概率为0;

而因为所有路径从点1开始,所以1的次数平地+1。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int const M=505*505;
int n,m,u[M],v[M],d[505];
double a[505][505],ans,x[505],w[M];
void gauss()
{
    for(int i=1;i<n;i++)
    {
        int k=i;
        for(int j=i+1;j<n;j++)
            if(fabs(a[j][i])>fabs(a[k][i]))k=j;//fabs
        if(k!=i)
            for(int l=i;l<=n+1;l++)swap(a[k][l],a[i][l]);
        for(int l=i+1;l<n;l++)
        {
            double r=a[l][i]/a[i][i];//不是k!!! 
            for(int t=i;t<=n+1;t++)
                a[l][t]-=r*a[i][t];
        }
    }
    x[n]=0;//!!!
    for(int i=n-1;i;i--)
    {
        for(int j=i+1;j<n;j++)a[i][n+1]-=a[i][j]*x[j];
        x[i]=a[i][n+1]/a[i][i];
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        d[u[i]]++;d[v[i]]++;
    }
    for(int i=1;i<=m;i++)
    {
        a[u[i]][v[i]]+=1.0/d[v[i]];
        a[v[i]][u[i]]+=1.0/d[u[i]];
    }
    for(int i=1;i<n;i++)a[i][i]=-1;
    a[1][n+1]=-1;
    gauss();
//    for(int i=1;i<=n;i++)
//        printf("x[%d]=%.3lf\n",i,x[i]);
    for(int i=1;i<=m;i++)
        w[i]=x[u[i]]/d[u[i]]+x[v[i]]/d[v[i]];
    sort(w+1,w+m+1);
    for(int i=1;i<=m;i++)
        ans+=(m-i+1)*w[i];
    printf("%.3lf",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/9052484.html