bzoj3143[Hnoi2013]游走 期望

显而易见,要最小化总分就要让期望经过次数最多的边权值最小。

而边的期望可以通过点的期望导出。

点的期望又可以通过和它相连的点的权值导出。

就可以列成一组方程。

再高斯消元一下就可以解出来了。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const double eps=1e-8;
inline double max(double x,double y){
	return x>y?x:y;
}
inline double abs(double x){
	return max(x,-x);
}
inline void swap(double &x,double &y){
	double z=x;x=y;y=z;
}
inline bool cmp(const double &a,const double &b){
	return a>b;
}
inline int read(){
	int x=0,f=1,ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-'){f=-1;}ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,m;
int u[250005],v[250005];
int map[505][505],du[505];
double a[505][505],res[505],val[250005];
inline void guess(){
	int i,j,k,r;
	double t;
	for(i=1;i<=n-1;i++){
		r=i;
		for(j=i+1;j<=n;j++)
			if(abs(a[j][i])>abs(a[r][i])+eps)
				r=j;
		for(j=i;j<=n+1;j++) swap(a[r][j],a[i][j]);
		for(j=i+1;j<=n;j++){
			t=a[j][i]/a[i][i];
			for(k=i;k<=n+1;k++) a[j][k]-=a[i][k]*t;
		}
	}
	for(i=n;i>0;i--){
		t=0;
		for(j=i+1;j<=n;j++) t+=a[i][j]*res[j];
		res[i]=(a[i][n+1]-t)/a[i][i];
	}
}
int main(){
	n=read(),m=read();
	int i,x,y,j;
	double ans=0;
	for(i=1;i<=m;i++){
		x=read(),y=read();u[i]=x,v[i]=y;
		map[x][y]=1;map[y][x]=1;du[x]++;du[y]++;
	}
	for(i=1;i<=n-2;i++)
		for(j=1;j<=n-1;j++){
			if(map[i][j]) a[i][j]=1.0/du[j];
			if(i==j) a[i][j]=-1;
		}
	a[n-1][n]=1;
	for(i=1;i<=n-2;i++) a[i][n]=(i-1)?0:(-1);
	for(i=1;i<=n-1;i++) a[n-1][i]=1.0*map[n][i]/du[i];
	n--;guess();
	for(i=1;i<=m;i++) val[i]=1.0/du[u[i]]*res[u[i]]+1.0/du[v[i]]*res[v[i]];
	sort(val+1,val+1+m,cmp);
	for(i=1;i<=m;i++) ans+=val[i]*i;
	printf("%.3lf\n",ans+eps);
	return 0;
}

  

posted @ 2017-02-25 20:26  古城独钓  阅读(181)  评论(0编辑  收藏  举报