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);
}