bzoj 3143: [Hnoi2013]游走(高斯消元)

3143: [Hnoi2013]游走

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 3171   Solved: 1390
[ Submit][ Status][ Discuss]

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


无向连通图

只要知道每条边经过的期望,就可以排序贪心了

而对于边e(u, v),经过这条边的期望为:经过u点的期望/u点度数+经过v点的期望/v点度数

所以这题还是求经过每个点次数的期望


设经过第i个点的期望次数为x[i],第i个点度数为in[i],那么有方程组

x[i] = ∑(x[j]/in[j]) (存在边e(i, j), j!=n)

但是1号点和n号点特殊,一开始就在1号点所以

x[1]-1 = ∑(x[j]/in[j]) (存在边e(1, j), j!=n)

而到了n号点就结束了所以

1-x[n] = ∑(x[j]/in[j]) (存在边e(n, j), j!=n),这也是为什么所有的方程都要j!=n

这样就可以列出n个n元方程,高斯消元求解即可

最后求出边期望贪心


#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef struct Road
{
	int x, y;
	double q;
	bool operator < (const Road &b) const
	{
		if(q<b.q)
			return 1;
		return 0;
	}
}Road;
Road s[250005];
double Jz[505][505], c[505], in[505];
void Gauss(int n, int m)
{
	int i, j, p, q, r;
	p = q = 1;
	while(p<=n && q<=m)
	{
		r = p;
		for(i=p+1;i<=n;i++)
		{
			if(fabs(Jz[i][q])>fabs(Jz[r][q]))
				r = i;
		}
		/*if(fabs(Jz[r][q])<eps)
			return 0;  ----方程无解*/
		for(i=q;i<=m;i++)
			swap(Jz[r][i], Jz[p][i]);
		swap(c[r], c[p]);
		c[p] /= Jz[p][q];
		for(i=q+1;i<=m;i++)
			Jz[p][i] /= Jz[p][q];
		for(i=1;i<=n;i++)
		{
			if(Jz[i][q] && i!=p)
			{
				for(j=q+1;j<=m;j++)
					Jz[i][j] -= Jz[p][j]*Jz[i][q];
				c[i] -= c[p]*Jz[i][q];
				Jz[i][q] = 0;
			}
		}
		q++, p++;
	}
	//return 1;
}
int main(void)
{
	int n, m, i;
	double ans;
	scanf("%d%d", &n, &m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d", &s[i].x, &s[i].y);
		in[s[i].x]++, in[s[i].y]++;
	}
	c[1] = -1, c[n] = 1;
	for(i=1;i<=n-1;i++)
		Jz[i][i] = -1;
	for(i=1;i<=m;i++)
	{
		Jz[s[i].x][s[i].y] += 1/in[s[i].y];
		Jz[s[i].y][s[i].x] += 1/in[s[i].x];
	}
	for(i=1;i<=n-1;i++)
		Jz[i][n] = 0;
	Jz[n][n] = 1;
	Gauss(n, n);
	for(i=1;i<=m;i++)
		s[i].q = c[s[i].x]/in[s[i].x]+c[s[i].y]/in[s[i].y];
	sort(s+1, s+m+1);
	ans = 0;
	for(i=1;i<=m;i++)
		ans += (m-i+1)*s[i].q;
	printf("%.3f\n", ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值