【BZOJ-3143】游走 高斯消元 + 概率期望

3143: [Hnoi2013]游走

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2264  Solved: 987
[Submit][Status][Discuss]

Description

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

Input

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

Output

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

Sample Input

3 3
2 3
1 2
1 3

Sample Output

3.333

HINT

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

Source

非官方数据

Solution

和 博物馆 那道题类似,列期望方程高斯消元得解。

如果对边进行处理,复杂度是$O((N^{2})^{3})$的,所以考虑利用点来求解边。

设未知数$X_{i}$表示第$i$号点的期望经过次数。那么显然有$X_{i}=\sum X_{j}$这样就可以列方程了。

显然有两个特例,必然从$1$号点出发,所以$X_{1}-1=\sum X_{j}$,以及必然从$N$号点结束,所以$X_{N}=0$,其余的可以得解。

对于一条边$<u,v>$,经过这条边的期望次数就是$\frac {X_{u}} {d[u]}+\frac {X_{v}}{d[v]}$,总期望最小,就是期望次数越小的边标号越大即可。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
int x=,f=; char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') f=-; ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-''; ch=getchar();}
return x*f;
}
#define MAXN 550
#define eps 1e-5
int N,M,mp[MAXN][MAXN],flag,d[MAXN];
double a[MAXN][MAXN],X[MAXN],p[MAXN*MAXN],ans;
inline void Debug()
{
puts("=========");
for (int i=; i<=N; i++,puts(""))
for (int j=; j<=N+; j++) printf("%.2lf ",a[i][j]);
puts("");
}
inline void Gauss()
{
flag=;
for (int i=; i<=N; i++)
{
int mx=i;
for (int j=i+; j<=N; j++)
if (abs(a[j][i])>abs(a[mx][i])) mx=j;
swap(a[i],a[mx]);
if (abs(a[i][i])<eps) {flag=-; continue;}
for (int j=i+; j<=N+; j++) if (abs(a[i][j])>) a[i][j]/=a[i][i];
a[i][i]=1.0; for (int j=i+; j<=N; j++)
{
for (int k=i+; k<=N+; k++)
a[j][k]-=a[j][i]*a[i][k];
a[j][i]=0.0;
}
// Debug();
}
for (int i=,f=; i<=N; i++,f=)
{
for (int j=; j<=N && f; j++)
if (abs(a[i][j])>eps) f=;
if (abs(a[i][M+])>eps && f) flag=;
}
if (flag==) return;
for (int i=N; i>=; i--)
{
X[i]=a[i][N+];
for (int j=i+; j<=N; j++) X[i]-=X[j]*a[i][j];
}
}
int main()
{
N=read(),M=read();
for (int i=,x,y; i<=M; i++) x=read(),y=read(),mp[x][y]=mp[y][x]=,d[x]++,d[y]++;
for (int i=; i<=N-; i++)
for (int j=; j<=N-; j++)
if (mp[i][j]) a[i][j]=-1.0/d[j];
a[][N+]=1.0;
for (int i=; i<=N; i++) a[i][i]=1.0;
// Debug();
Gauss();
for (int i=,tot=; i<=N; i++)
for (int j=i+; j<=N; j++)
if (mp[i][j]) p[++tot]=X[i]/d[i]+X[j]/d[j];
sort(p+,p+M+);
// for (int i=1; i<=M; i++) printf("%.2lf ",p[i]); puts("");
for (int i=; i<=M; i++) ans+=p[i]*(M-i+);
printf("%.3lf\n",ans);
return ;
}
上一篇:HDU 1159 Common Subsequence 最长公共子序列


下一篇:ADO.NET复习总结(1)--ADO.NET基础介绍