BZOJ 3143 (概率+高斯消元)

3143: [Hnoi2013]游走

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 959   Solved: 405
[ Submit][ Status]

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

【题解】这道题,首先我们分析,为了使总分期望最小,我们就要把经过几率最大的边附上最小的权值,然后问题就转移为了———如何求一条边的概率,这又要先从点的概率着手。

  那么一个点的概率是多少呢?它(不是起始位置,1)是由它周围的点的概率再通过计算概率得到的,这样我们设每个点的概率 x ,就可以列出若干个个方程。我们发现特殊的点 n 对其它点的贡献是0,还有 1 号点由于是起始点的原因,概率还要加上 1。用高斯消元解决。然后就可以计算出一条边的概率为:  他两端点的概率*选择这条边的概率。最后计算权值。


#include
#include
#include

using namespace std;
const double eps=1e-9;
int n,g[505][505],u[505*505],v[505*505],d[505];
double f[505][505],val[505*505],res[505];
//g数组用于表示两点之间是否有边;u,v数组分别记录一条边的两端;d记录一个点相连的边;f是高斯消元中的矩阵。
inline void add(int x,int y)
{
	g[x][y]++;g[y][x]++;d[x]++;d[y]++;
}

inline double absx(double x){return x<0?(-x):x;}
inline void swapx(double &x,double &y){double t=x;x=y;y=t;}
//高斯消元; 
inline void guass()
{
	for(int i=1;i<=n-1;i++)
	{
		int t=i;
		for(int j=i+1;j<=n;j++)
		 if(absx(f[j][i])>absx(f[t][i])+eps)t=j;
		
		if(t-i)for(int p=i;p<=n+1;p++)swapx(f[i][p],f[t][p]);
		for(int j=i+1;j<=n;j++)
		{
			 double t=f[j][i]/f[i][i];
			 for(int p=i;p<=n+1;p++)f[j][p]-=f[i][p]*t;
		}
	}
	
	for(int i=n;i;i--)
	{
		double t=0;
		for(int j=i+1;j<=n;j++)t+=f[i][j]*res[j];
		res[i]=(f[i][n+1]-t)/f[i][i]; 
	}
}

bool cmp(double x,double y){return x>y;}

int main()
{
	int m;
	memset(g,0,sizeof(g));
	memset(d,0,sizeof(d));
	memset(f,0,sizeof(f));
	
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);
		add(u[i],v[i]);
	}
	
	for(int i=1;i<=n-2;i++)
	 for(int j=1;j<=n-1;j++)
	  f[i][j]=(i-j)?(1.0*g[i][j]/d[j]):(-1);
	  
	for(int i=1;i<=n-2;i++)f[i][n]=(i-1)?0:(-1);
	f[n-1][n]=1;
	for(int i=1;i<=n-1;i++)f[n-1][i]=1.0*g[n][i]/d[i];
	
	n--;
	memset(res,0,sizeof(res));
	guass();
		
	for(int i=1;i<=m;i++)val[i]=1.0/d[u[i]]*res[u[i]]+1.0/d[v[i]]*res[v[i]];
	
	sort(val+1,val+1+m,cmp);
	double ans=0;
	for(int i=1;i<=m;i++)ans+=val[i]*i;
	printf("%.3lf\n",ans+eps);
	return 0;
}



你可能感兴趣的:(BZOJ,高斯消元法)