首页 > 代码库 > BZOJ 3143 HNOI2013 游走 期望DP+高斯消元

BZOJ 3143 HNOI2013 游走 期望DP+高斯消元

题目大意:给定一个无向连通图,我们需要给每条边附一个1~m的不重复的权值,使1到n的期望权值和最小

首先贪心思想是求出每条边的期望经过次数 然后对期望值最小的边附加m的权值,第二小的边附加m-1的权值,以此类推。

令f[i]为第i个点的期望经过次数 那么每条边的期望经过次数就是f[x]/d[x]+f[y]/d[y] 其中d[x]表示x的度数

那么显然有:

f[1]=1+Σ[1->j]f[j]/d[j]

f[i]=Σ[i->j]f[j]/d[j] (2<=i<=n-1)

其中f[n]应该等于1,但是由于进入了点n之后就不再出来,因此n点对周围的边和点的贡献都是0,不妨令f[n]=0

那么就有n个未知数n个方程 高斯消元求解即可

然后代入即可出解 注意求的是最小值不是最大值 因此最小的期望值应该乘最大的边权 不要弄反

边集又开小了- - 我是怎么了最近- -

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define M 550
using namespace std;
struct abcd{
	int to,next;
}table[M*M<<1];
int head[M],tot;
int n,m,degree[M];
double f[M][M],a[M];
double expectation[M*M],ans;
void Add(int x,int y)
{
	degree[x]++;
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
void Gauss_Elimination()
{
	int i,j,k;
	for(i=1;i<=n;i++)
	{
		k=i;
		for(j=i+1;j<=n;j++)
			if( fabs(f[j][i])>fabs(f[k][i]) )
				k=j;
		for(j=i;j<=n+1;j++)
			swap(f[i][j],f[k][j]);
		for(k=i+1;k<=n;k++)
		{
			double temp=-f[k][i]/f[i][i];
			for(j=i;j<=n+1;j++)
				f[k][j]+=f[i][j]*temp;
		}
	}
	for(i=n;i;i--)
	{
		for(j=i+1;j<=n;j++)
			f[i][n+1]-=f[i][j]*a[j];
		a[i]=f[i][n+1]/f[i][i];
	}
}
int main()
{
	int i,x,y;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		Add(x,y),Add(y,x);
	}
	for(x=1;x<n;x++)
	{
		f[x][x]-=1;
		if(x==1) f[x][n+1]=-1;
		for(i=head[x];i;i=table[i].next)
			f[x][table[i].to]+=1.0/degree[table[i].to];
	}
	f[n][n]=1;f[n][n+1]=0;
	Gauss_Elimination();
	for(i=1;i<=m;i++)
	{
		x=table[i+i-1].to;
		y=table[i<<1].to;
		expectation[i]=a[x]/degree[x]+a[y]/degree[y];
	}
	sort(expectation+1,expectation+m+1);
	for(i=1;i<=m;i++)
		ans+=(m-i+1)*expectation[i];
	cout<<fixed<<setprecision(3)<<ans<<endl;
	return 0;
}


BZOJ 3143 HNOI2013 游走 期望DP+高斯消元