[bzoj3143] [HNOI2013]游走

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

Solution

问题可以转化为求每条边期望被经过多少次,然后期望大的编号小就行了。

对于每条边,它连接的两个点每次有一定概率走这条边,所以这条边的期望就是:

\[\frac{E(u)}{deg(u)}+\frac{E(v)}{deg(v)} \]

所以问题转化为求一个点期望被经过多少次。

\(E(x)\)表示这个点期望经过次数,可以列出式子:

\[E(x)=\sum_{(x,v)\in Edge} \frac{E(v)}{deg(v)} \]

然后列方程解出来就行了。

对于最后一个点,走到它就结束了,所以忽略\(n\)号点连出来的边。

同样,算边的期望时也忽略。

对于第一个点,期望应为:

\[E(1)=\sum_{(1,v)\in Edge}\frac{E(v)}{deg(v)}+1 \]

因为一开始就经过了一次。

#include<bits/stdc++.h>
using namespace std;

#define ONLINE_JUDGE

#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
#endif

namespace fast_IO {
	char buf[1<<21],*p1=buf,*p2=buf;

	template <typename T> inline void read(T &x) {
		x=0;T f=1;char ch=getchar();
		for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
		for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
	}
	template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
		read(x),read(args...);
	}

	char buf2[1<<21],a[80];int p,p3=-1;

	inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
	template <typename T> inline void write(T x) {
		if(p3>(1<<20)) flush();
		if(x<0) buf2[++p3]='-',x=-x;
		do {a[++p]=x%10+48;} while(x/=10);
		do {buf2[++p3]=a[p];} while(--p);
		buf2[++p3]='\n';
	}
	template <typename T,typename... Args> inline void write(T x,Args ...args) {
		write(x),write(args...);
	}
}

using fast_IO :: read;
using fast_IO :: write;
using fast_IO :: flush;

#define lf double 

const lf eps = 1e-5;
const int maxn = 5e2+10;

lf f[maxn][maxn],w[maxn*maxn];
int n,m,head[maxn],tot,deg[maxn];
struct edge{int to,nxt;}e[maxn*maxn];

void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}

void gauss() {
	for(int i=1;i<=n;i++) {
		if(fabs(f[i][i])<eps) 
			for(int j=i;j<=n;j++)
				if(fabs(f[j][i])>eps) {
					for(int k=1;k<=n+1;k++)
						swap(f[i][k],f[j][k]);
					break;
				}
		for(int j=i+1;j<=n;j++) {
			lf t=f[j][i]/f[i][i];
			for(int k=i;k<=n+1;k++) f[j][k]=f[j][k]-f[i][k]*t;
		}
	}
	for(int i=n;i;i--) {
		lf tmp=f[i][n+1];
		for(int j=i+1;j<=n;j++) tmp-=f[j][j]*f[i][j];
		tmp/=f[i][i];f[i][i]=tmp;
	}
}

int main() {
	read(n,m);
	for(int i=1,x,y;i<=m;i++) read(x,y),ins(x,y),deg[x]++,deg[y]++;
	for(int x=1;x<=n;x++) {
		for(int i=head[x];i;i=e[i].nxt)
			if(e[i].to!=n) f[x][e[i].to]-=1.0/deg[e[i].to];
		f[x][x]+=1.0;
	}f[1][n+1]+=1.0;
	gauss();f[n][n]=0;
	for(int i=1;i<=tot;i++) 
		w[(i+(i&1))>>1]+=f[e[i].to][e[i].to]/(lf)deg[e[i].to];
	sort(w+1,w+tot/2+1);lf ans=0;
	for(int i=1;i<=tot/2;i++) ans+=w[i]*(lf)(tot/2-i+1);
	printf("%.3lf\n",ans);
	flush();
	return 0;
}
posted @ 2018-12-29 16:14  Hyscere  阅读(109)  评论(0编辑  收藏  举报