[HNOI2013]游走

Posted by Dispwnl on February 17, 2019

题目

题目描述

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

输入输出格式

输入格式:

第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1<=u,v<=N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N<=10,100%的数据满足2<=N<=500且是一个无向简单连通图。

输出格式:

仅包含一个实数,表示最小的期望值,保留3位小数。

输入输出样例

输入样例#1:

3 3
2 3
1 2
1 3

输出样例#1:

3.333

说明

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

题解

$f_i$表示$i$点经过的期望次数,首先能列出方程:$f_i=\sum_{(i,j)\in E}\frac{f_j}{du_j}$

这样就可以用高斯消元搞了,注意$f_n=0$,$f_1$的初始值为$1$

已知每个点的期望可以搞出每条边的期望$E(u,v)=\frac{f_u}{du_u}+\frac{f_v}{du_v}$

贪心填编号,期望大的边编号小

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<cmath>
# include<algorithm>
using namespace std;
const int N=505;
const double fs=1e-7;
struct p{
	int x,y,fr;
}c[N*N];
int n,m,num,cnt;
double Ans;
int h[N],du[N];
double _ans[N*N];
double f[N][N];
int read()
{
	int x(0);
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);x=x*10+ch-48,ch=getchar());
	return x;
}
void add(int x,int y)
{
	++du[x],++du[y]; 
	c[++num]=(p){h[x],y,x},h[x]=num;
	c[++num]=(p){h[y],x,y},h[y]=num;
}
void Gauss()
{
    for(int i=1,id;i<=n;++i)
      {
        id=i;
        for(int j=i+1;j<=n;++j)
          if(fabs(f[j][i])>fabs(f[id][i])) id=j;
        if(fabs(f[id][i])<fs) return;
        double A=f[id][i];
		for(int j=i;j<=n+1;++j)
          swap(f[i][j],f[id][j]),f[i][j]/=A;
        for(int j=1;j<=n;++j)
          if(i!=j)
          {
          	A=f[j][i];
          	for(int k=i;k<=n+1;++k)
          	  f[j][k]-=f[i][k]*A;
		  }
      }
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=m;++i)
	  add(read(),read());
	for(int i=1;i<n;++i)
	  {
	  	f[i][i]=1;
	  	for(int j=h[i];j;j=c[j].x)
	      f[i][c[j].y]=-1/double(du[c[j].y]);
	  }
	f[n][n]=f[1][n+1]=1,Gauss();
	for(int i=1;i<=num;i+=2)
	  _ans[++cnt]=f[c[i].y][n+1]/du[c[i].y]+f[c[i].fr][n+1]/du[c[i].fr];
	sort(_ans+1,_ans+1+cnt);
	for(int i=1;i<=m;++i)
	  Ans+=_ans[i]*(m-i+1);
	return printf("%.3lf",Ans),0;
}