bzoj3143-游走
题目
一个无向连通图,顶点从\(1\)编号到\(N\)\((n\le 500)\),边从\(1\)编号到\(M\)。
小Z在该图上进行随机游走,初始时在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这\(M\)条边进行编号,使得小Z获得的总分的期望值最小。
分析
又是一个期望问题。我们要求的总分期望值,其实就是每条边的编号乘上经过这条边的期望次数。由于经过边的期望次数不好求,我们转而求经过每个点的期望次数,再算回边即可。方法类似期望dp,但由于这是一个无向图,无法根据拓扑序直接递推,所以我们要列方程。注意到,很多图中求每个点或每条边的信息的问题都会因为环的问题而无法直接解决,这时候列方程是很好的办法。
到达一个点的期望次数就是从连接到它的点走过来的期望次数,其中1号点的期望次数要加一,因为一开始就在那里,移项之后变成常数项-1。n号点的期望次数就是1,因为走到n点的时候就结束了。列方程求解即可。
代码
这里精度是大坑。精度至少开到\(eps=1e-12\),但long double倒用不上。
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
typedef double lb;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int maxn=505;
const int maxm=maxn*maxn;
const lb eps=1e-12;
struct edge {
int v,nxt;
} e[maxm];
int h[maxn],tot=0;
void add(int u,int v) {
e[++tot]=(edge){v,h[u]};
h[u]=tot;
}
int d[maxn];
lb a[maxn][maxn],ep[maxn],ee[maxm];
struct bian {
int u,v;
} ed[maxm];
int tt=0;
inline bool no(lb x) {
return fabs(x)<eps;
}
void elm(int n) {
for (int i=1;i<n;++i) {
if (no(a[i][i])) {
for (int j=i+1;j<=n;++j) if (!no(a[j][i])) {
for (int k=1;k<=n+1;++k) swap(a[i][k],a[j][k]);
break;
}
}
for (int j=i+1;j<=n;++j) if (!no(a[j][i])) {
lb tmp=a[j][i]/a[i][i];
for (int k=1;k<=n+1;++k) a[j][k]-=tmp*a[i][k];
}
}
for (int i=n;i>1;--i) {
if (no(a[i][i])) {
for (int j=i-1;j>0;--j) if (!no(a[j][i])) {
for (int k=1;k<=n+1;++k) swap(a[i][k],a[j][k]);
break;
}
}
for (int j=i-1;j>0;--j) if (!no(a[j][i])) {
lb tmp=a[j][i]/a[i][i];
for (int k=1;k<=n+1;++k) a[j][k]-=a[i][k]*tmp;
}
}
for (int i=1;i<=n;++i) if (no(a[i][i])) exit(0);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("my.out","w",stdout);
#endif
int n=read(),m=read();
for (int i=1;i<=m;++i) {
int u=read(),v=read();
ed[++tt]=(bian){u,v};
add(u,v),add(v,u),d[u]++,d[v]++;
}
for (int i=1;i<n;++i) {
a[i][i]=-1;
for (int j=h[i],v=e[j].v;j;j=e[j].nxt,v=e[j].v) if (v!=n) a[i][v]+=(lb)1.0/(lb)d[v];
}
a[1][n]=-1;
elm(n-1);
for (int i=1;i<n;++i) ep[i]=a[i][n]/a[i][i];
for (int i=h[n],v=e[i].v;i;i=e[i].nxt,v=e[i].v) ep[n]+=ep[v]/d[v];
for (int i=1;i<=tt;++i) {
if (ed[i].v==n) swap(ed[i].u,ed[i].v);
ee[i]=ep[ed[i].v]/d[ed[i].v];
if (ed[i].u!=n) ee[i]+=ep[ed[i].u]/d[ed[i].u];
}
sort(ee+1,ee+tt+1);
lb ans=0;
for (int i=1;i<=tt;++i) ans+=ee[i]*(tt-i+1);
printf("%.3lf\n",ans);
}