P3232 [HNOI2013]游走

[HNOI2013]游走 

题目简述:

给一个无向图,小Z从一到N随机游走获得分数,每条边对分数的贡献是其编号,求总分期望的最小值,复杂度O(n^{3})即可。

解析:

贪心的话可以想到应该让期望小的边配编号大的边,那么我们要做的是就是处理出边的期望值。

考虑一条边一定是有他的两个顶点转移过来的,所以边的期望=两顶点的期望和。每一个点的期望又可以从它相邻的所有点转移过来,设这个点是x,与它相邻的点是 i ;(别忘了乘概率)

f[x]=\sum \frac{f[i]}{du[i]}

接下来的操作就很秀了:高斯消元解方程。这里如果列 n 组,铁定是解不出来的,但是注意到:小Z在N点结束游走,所以这里的方程只有n-1组,加上N点的数据刚刚好解出来。

#include<bits/stdc++.h>
using namespace std;
int n,m,tot,du[510],head[510];
int u[1250010],v[1250010];
struct node{int to,next;}e[1250010];
void add_edge(int u,int v)
{
	e[++tot].to=v;
	e[tot].next=head[u];
	head[u]=tot;
}
double a[510][510],b[510],x[510],f[1250010],ans;
void gauss(int n)
{
	for(int i=1;i<=n;i++)
	{
		int maxx=i;
		for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[maxx][i])) maxx=j;
		if(i!=maxx) swap(a[i],a[maxx]),swap(b[i],b[maxx]);
		for(int j=i+1;j<=n;j++)
		{
			double tmp=a[j][i]/a[i][i];
			b[j]-=tmp*b[i];
			for(int k=i;k<=n;k++) a[j][k]-=tmp*a[i][k];
		}
	}
	for(int i=n;i;i--)
	{
		for(int j=i+1;j<=n;j++) b[i]-=x[j]*a[i][j];
		x[i]=b[i]/a[i][i];
	}
}
signed main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u[i],&v[i]);
		add_edge(u[i],v[i]);
		add_edge(v[i],u[i]);
		du[u[i]]++;
		du[v[i]]++;
	}
	for(int uu=1;uu<n;uu++)
	{
		a[uu][uu]=1;
		for(int j=head[uu];j;j=e[j].next)
		{
			int vv=e[j].to;
			if(vv!=n) a[uu][vv]=-1.0/du[vv];
		}
	}
	b[1]=1;
	gauss(n-1);
	//f[x]=sum(f[i]/num[i]); 
	for(int i=1;i<=m;i++)
	{
		if(u[i]!=n) f[i]+=1.0*x[u[i]]/du[u[i]];
		if(v[i]!=n) f[i]+=1.0*x[v[i]]/du[v[i]];
	}
	sort(f+1,f+m+1);//小期望对应大编号(贪心) 
	for(int i=1;i<=m;i++) ans+=(m-i+1)*f[i];
	printf("%.3lf",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiyuping24

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值