[HNOI2013]游走

一、题目

点此看题

题目:

n n n个点, m m m条边的联通无向图,边的权值从 1 1 1 m m m,一开始在1号点,每次等概率走向当前点的邻接点。问怎么样给边赋值,使得走到 n n n号点时期望走过的边权和最小。

数据范围:

2 ≤ N ≤ 500 2\leq N\leq 500 2N500

二、解法

UPD:2020/4/6 \text{UPD:2020/4/6} UPD2020/4/6,发现以前写的博客有问题,故来重做一波。

首先我们想求出每条边出现的期望次数,这样方便编号,求出每条边的出现次数可以转化为求出点的期望出现次数 E ( i ) E(i) E(i),这样一条边 ( u , v ) (u,v) (u,v)就可以表示为:
E ( u ) d e g [ u ] + E [ v ] d e g [ v ] \frac{E(u)}{deg[u]}+\frac{E[v]}{deg[v]} deg[u]E(u)+deg[v]E[v]如何求出 E ( i ) E(i) E(i)呢,一个显然的思路是高斯消元,数据范围也允许我们这样做,先列出一串柿子:
E ( i ) = { ∑ ( i , j ) ∈ e d g e , j ≠ n E ( j ) d e g [ j ] + 1 i = 1 ∑ ( i , j ) ∈ e d g e , j ≠ n E ( j ) d e g [ j ] 1 < i < n E(i)=\begin{cases}\sum_{(i,j)\in edge,j\not=n}\frac{E(j)}{deg[j]}+1&i=1\\\sum_{(i,j)\in edge,j\not=n}\frac{E(j)}{deg[j]}&1<i<n\end{cases} E(i)={(i,j)edge,j=ndeg[j]E(j)+1(i,j)edge,j=ndeg[j]E(j)i=11<i<n然后移个项就可做了,算出边期望之后从大到小排序,然后 1 1 1 m m m挨个赋值即可,时间复杂度 O ( n 3 ) O(n^3) O(n3)

#include <cstdio>
#include <iostream>
#include <algorithm>
#define eps 1e-7
using namespace std;
const int MAXN = 505;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m;
bool b[MAXN][MAXN];
double a[MAXN][MAXN],q[MAXN*MAXN],deg[MAXN];
struct edge
{
	int u,v;
}e[MAXN*MAXN];
bool cmp(double a,double b)
{
	return a>b;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		b[u][v]=b[v][u]=1;
		e[i]=edge{u,v};
		deg[u]+=1.0;deg[v]+=1.0;
	}
	a[1][n]=1.0;
	for(int i=1;i<n;i++)
	{
		a[i][i]=1.0;
		for(int j=1;j<n;j++)
			if(b[i][j])
				a[i][j]=-1.0/deg[j];
	}
	for(int i=1;i<n;i++)
	{
		int Max=i;
		for(int r=i+1;r<n;r++)
			if(a[r][i]-a[Max][i]>eps)
				Max=r;
		if(i^Max)swap(a[i],a[Max]);
		for(int j=n;j>=i;j--)
			a[i][j]/=a[i][i];
		for(int j=1;j<n;j++)
		{
			if(i==j || !a[j][i]) continue;
			double tmp=a[j][i]/a[i][i];
			for(int k=i+1;k<=n;k++)
				a[j][k]-=a[i][k]*tmp;
			a[j][i]=0;
		}
	}
	for(int i=1;i<=m;i++)
	{
		int u=e[i].u,v=e[i].v;
		if(u^n) q[i]+=a[u][n]*(1.0/deg[u]);
		if(v^n) q[i]+=a[v][n]*(1.0/deg[v]);
	}
	sort(q+1,q+1+m,cmp);
	double ans=0;
	for(int i=1;i<=m;i++)
		ans+=q[i]*i;
	printf("%.3lf",ans);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值