P3232[HNOI2013]游走

题意

给定一个 nnn 个点 mmm 条边的无向连通图,顶点从 111 编号到 nnn,边从 111 编号到 mmm。

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

思路

因为路径中有环,所以不能直接递推,所以要先把期望的式子列出来,再进行高斯消元求解,求完解后,按期望大小进行排序,期望次数 大的就有小的标号,小的用大的标号,依次后求出答案

代码

#include<bits/stdc++.h>
using namespace std;
int du[503],num,head[503];
double b[503],f[250004];
double g[505][505],x[503];
struct{
    int ne,to,w;
}a[250004];
void lian(int from,int to){
    num++;
    a[num].ne=head[from];
    a[num].to=to;
    head[from]=num;
}
void guss(int n){
    int i,j,k,Max;
    double p;
    for(i=1;i<=n;i++){
        Max=i;
        for(j=i+1;j<=n;j++)
            if(fabs(g[Max][i])<fabs(g[j][i]))Max=j;
        for(j=i;j<=n;j++)
            swap(g[i][j],g[Max][j]);
        swap(b[i],b[Max]);
        for(j=1;j<=n;j++)if(i!=j&&g[j][i]!=0){
            p=g[j][i]/g[i][i];b[j]-=b[i]*p;
            for(k=1;k<=n;k++)
                g[j][k]-=g[i][k]*p;
        }
    }
}
int main(){
    int n,m,to,i,j,u,v,w,from;
    double ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        lian(u,v);lian(v,u);
        du[u]++;du[v]++;
    }
    for(i=1;i<n;i++){
        g[i][i]=1;
        for(j=head[i];j;j=a[j].ne){
            to=a[j].to;
            if(to!=n)g[i][to]=-1.0/du[to];
        }
    }b[1]=1;
    guss(n-1);
    for(i=1;i<n;i++){
        if(g[i][i]!=0)x[i]=b[i]/g[i][i];
    //    cout<<du[i]<<" ";
    }
//    for(i=1;i<=n;i++)
    //    for(j=1;j<=n;j++)cout<<g[i][j]<<" ";
    for(i=2;i<=num;i+=2){
        to=a[i].to;from=a[i-1].to;
        if(to!=n)f[i/2]=x[to]/du[to];
        if(from!=n)f[i/2]+=x[from]/du[from];
    }
    sort(f+1,f+num/2+1);
    for(i=1;i<=num/2;i++)
        ans+=f[i]*(num/2-i+1);
    printf("%.3lf\n",ans);
    return 0;
}

 

posted @ 2020-11-18 21:05  Jessica_Cao  阅读(106)  评论(0编辑  收藏  举报