[BZOJ]3143: [Hnoi2013]游走 期望+高斯消元

Description

一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

题解:

易得只要得知每条边的期望走过次数,就可以得到答案,但是这个并不好求。转化一下,求每个点的期望走过次数,那么每条边的也可以被算出来了。算点的期望简单很多,注意一下两个特殊点:1号点,算的时候要+1;n号点,期望走过次数为1,然后高斯消元一下就好了,跟BZOJ2337的套路差不多。另外,这道题卡精度!eps开1e-8会WA!1e-10才AC!

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=510;
const long double eps=1e-10;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct Edge{int x,y,next;}e[Maxn*Maxn*2];
int last[Maxn],len=0;
void ins(int x,int y)
{
    int t=++len;
    e[t].x=x;e[t].y=y;e[t].next=last[x];last[x]=t;
}
long double a[Maxn][Maxn],dis[Maxn*Maxn];
int ex[Maxn*Maxn],ey[Maxn*Maxn];
void gauss()
{
    for(int i=1;i<n;i++)
    {
        if(abs(a[i][i])<=eps)
        {
            for(int j=i+1;j<n;j++)
            if(abs(a[j][i])>eps)
            {
                for(int k=i;k<=n+1;k++)swap(a[j][k],a[i][k]);
                break;
            }
        }
        for(int j=i+1;j<n;j++)
        if(abs(a[j][i])>eps)
        {
            long double t=a[j][i]/a[i][i];
            for(int k=i;k<=n+1;k++)a[j][k]-=t*a[i][k];
        }
    }
    for(int i=n-1;i;i--)
    {
        for(int j=i+1;j<=n;j++)
        a[i][n+1]-=a[i][j]*a[j][n+1];
        a[i][n+1]/=a[i][i];
    }
}
int degree[Maxn];
int main()
{
    memset(a,0,sizeof(a));
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();ex[i]=x;ey[i]=y;
        ins(x,y);ins(y,x);degree[x]++;degree[y]++;
    }
    a[1][n+1]=-1.0;a[n][n]=a[n][n+1]=1.0;
    for(int x=1;x<n;x++)
    {
        a[x][x]=-1.0;
        for(int i=last[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(y!=n)a[y][x]+=(1.0/(long double)(degree[x]));
        }
    }
    gauss();
    for(int i=1;i<=m;i++)
    {
        dis[i]=0.0;
        if(ex[i]!=n)dis[i]+=a[ex[i]][n+1]/(long double)(degree[ex[i]]);
        if(ey[i]!=n)dis[i]+=a[ey[i]][n+1]/(long double)(degree[ey[i]]);
    }
    sort(dis+1,dis+1+m);
    long double ans=0.0;
    for(int i=m;i;i--)
    ans+=dis[m-i+1]*(long double)(i);
    printf("%.3Lf",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值