[Hnoi2013]游走
Time Limit: 10 Sec
Memory Limit: 128 MB
Description
一个无向连通图,顶点从1编号到
N
N
,边从1编号到。
小
Z
Z
在该图上进行随机游走,初始时小在1号顶点,每一步小
Z
Z
以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小到达
N
N
号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这条边进行编号,使得小
Z
Z
获得的总分的期望值最小。
Input
第一行是正整数和 M M ,分别表示该图的顶点数 和边数,接下来行每行是整数 u,v u , v ( 1≤u 1 ≤ u , v≤N v ≤ N ),表示顶点 u u 与顶点之间存在一条边。 输入保证30%的数据满足 N≤10 N ≤ 10 ,100%的数据满足 2≤N≤500 2 ≤ N ≤ 500 且是一个无向简单连通图。
Output
仅包含一个实数,表示最小的期望值,保留3位小数。
Sample Input
3 3
2 3
1 2
1 3
Sample Output
3.333
解:
不会这道题估计是因为不会期望概率,woc,这玩意儿真的巨难。
首先我们会发现一个很简单的贪心结论:把边走过的期望从小到大排序,最小的乘上
m
m
,次小的乘上,以此类推,得到的就是最优解。显而易见。
然后我们会发现边走过的期望次数是很难求的,而且边是没有个数限制的,我们只能考虑点。(用
di
d
i
表示第
i
i
号点的度数,用表示第
i
i
号点的期望经过次数)
然后有一个点的经过次数转化为边的经过次数的式子:。因为每条边只能从它左右两个点经过,也是很显然的。
最后我们只需要考虑每个点的经过次数,
fi=Σi−>jfjdj(1≤i,j≤n−1)
f
i
=
Σ
i
−
>
j
f
j
d
j
(
1
≤
i
,
j
≤
n
−
1
)
。还是同一个思想,一个点只能从它相邻的点转移过来。
然后由于1号点为出发点,所以还要加一个1,
n
n
号点由于走到会停止,所以永远会是0。
n-1个方程,n-1个未知数,而且数据范围也允许我们使用高斯消元,那么这道题就完成了。
贴波代码(高斯消元要有独特的卡常技巧,记得有0就要
continue
c
o
n
t
i
n
u
e
,否则会巨慢):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct lxy{
int to,next;
}b[250005];
int n,m,cnt,x,y;
int head[505],deg[505];;
double a[505][505];
double ans[500005];
double ret;
double eps=1e-13;
void gs()
{
for(int i=1;i<n;i++)
{
double mmm=0;int pos;
for(int j=i;j<n;j++)
if(fabs(a[j][i])>mmm)
pos=j,mmm=a[j][i];
for(int j=0;j<n;j++)
swap(a[i][j],a[pos][j]);
mmm=a[i][i];
for(int j=n-1;j>=i;j--)
a[i][j]=a[i][j]/mmm;
a[i][0]=a[i][0]/mmm;
for(int j=1;j<n;j++)
if(j!=i)
{
mmm=a[j][i];
if(fabs(mmm)<eps) continue;
for(int k=n-1;k>=i;k--)
a[j][k]=a[j][k]-a[i][k]*mmm;
a[j][0]=a[j][0]-a[i][0]*mmm;
}
}
}
void add(int op,int ed)
{
b[++cnt].next=head[op];
b[cnt].to=ed;
head[op]=cnt;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
cnt=0;
for(int j=1;j<=n;j++)
for(int i=head[j];i!=-1;i=b[i].next)
deg[j]++;
a[1][0]=-1;
for(int j=1;j<n;j++)
{
for(int i=head[j];i!=-1;i=b[i].next)
if(b[i].to!=n)
a[j][b[i].to]=(double) 1/deg[b[i].to];
a[j][j]=-1;
}
gs();
double t;
for(int j=1;j<=n;j++)
for(int i=head[j];i!=-1;i=b[i].next)
{
t=a[j][0]/deg[j]+a[b[i].to][0]/deg[b[i].to];
ans[++cnt]=t;
}
sort(ans+1,ans+1+2*m);
int p=m;
for(int i=1;i<=2*m;i=i+2)
ret+=ans[i]*p,p--;
printf("%.3f",ret);
}