2018.09.23 bzoj3143: [Hnoi2013]游走(dp+高斯消元)

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/82824101

传送门
显然只需要求出所有边被经过的期望次数,然后贪心把边权小的边定城大的编号。
所以如何求出所有边被经过的期望次数?
显然这只跟边连接的两个点有关。
于是我们只需要求出两个点被经过的期望次数。
对于一个点 u u ,它被经过的期望次数 f [ u ] = v f [ v ] / d u [ v ] f[u]=\sum _v f[v]/du[v]
这是一个环上的递推式,我们可以用高斯消元解方程组。
代码:

#include<bits/stdc++.h>
#define N 505
#define M 250005
using namespace std;
int n,m,du[N];
double matrix[N][N],f[N],ans=0.0;
bool tran[N][N];
struct edge{int u,v;double w;}e[M];
inline bool cmp(edge a,edge b){return a.w<b.w;}
inline void guass(int n){
	for(int i=1;i<=n;++i){
		int tmp=i;
		for(int j=i+1;j<=n;++j)if(fabs(matrix[j][i])>fabs(matrix[tmp][i]))tmp=j;
		if(tmp^i)swap(matrix[i],matrix[tmp]);
		for(int j=i+1;j<=n;++j){
			double ttmp=matrix[j][i]/matrix[i][i];
			for(int k=i;k<=n+1;++k)matrix[j][k]-=matrix[i][k]*ttmp;
		}
	}
	for(int i=n;i;--i){
		for(int j=i+1;j<=n;++j)matrix[i][n+1]-=matrix[i][j]*f[j];
		f[i]=matrix[i][n+1]/matrix[i][i];
	}
}
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		++du[e[i].u=read()],++du[e[i].v=read()];
		tran[e[i].u][e[i].v]=tran[e[i].v][e[i].u]=true;
	}
	matrix[1][n]=1;
	for(int i=1;i<=n;++i)matrix[i][i]=1;
	for(int i=1;i<n;++i)for(int j=1;j<n;++j)if(tran[i][j])matrix[i][j]=-1.0/du[j];
	guass(n-1);
	for(int i=1;i<=m;++i)e[i].w=f[e[i].u]/du[e[i].u]+f[e[i].v]/du[e[i].v];
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;++i)ans+=e[i].w*(m-i+1);
	printf("%.3lf",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/82824101