游走[HNOI2013][期望Dp+高斯消元]

文章目录

题目

Vjudge
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。

无向图

思路

考虑
a n s = i = 1 m E i v a l i ans=\sum_{i=1}^m{E_i*val_i}
其中 E i E_i 为第 i i 条边经过期望次数
显然 E i E_i 大的配 v a l i val_i 小的
如果我们的边拆点的话点太多了,因为这种有依赖性的 d p dp 一般要用高斯消元
考虑 E i E_i 的期望可以利用期望的线性性使得

E i = f u d e u + f v d e v E_i=\frac{f_u}{de_u}+\frac{f_v}{de_v} ,其中 f u , f v f_u,f_v 为经过点的期望次数

转化为求点的经过期望次数
根据期望的线性性列出状态转移方程:
f u = v t o u f v d e v + [ u = = 1 ] f_u=\sum_{v\in to_u}\frac{f_v}{de_v}+[u==1]
f 1 f_1 会加1是因为一来就在 1 1
根据 E i E_i 的变形可以推出 f n = 0 f_n=0 (可理解为随机游走时候一碰到 n n 点就消失了)
变形后高斯消元即可

代码


#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstring>
#include<climits>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
inline int read() {
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c==EOF)exit(0);if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 500
#define INF 0x3f3f3f3f
int de[MAXN+5];
vector<int> G[MAXN+5];
double a[MAXN+5][MAXN+5];
double Abs(double x){return x>0?x:-x;}
void Print(int n){
	for(int i=1;i<=n;i++,puts(""))
		for(int j=1;j<=n+1;j++)
			printf("%f ",a[i][j]);
	return ;
}
void Guass(int n){
	//Print(n);
	for(int i=1;i<=n;i++){
		int r=i;
		for(int j=i;j<=n;j++)
			if(Abs(a[r][i])<Abs(a[j][i]))
				r=j;
		swap(a[r],a[i]);
		for(int j=1;j<=n;j++)
			if(j!=i){
				double tmp=a[j][i]/a[i][i];
				a[j][i]=0;
				for(int k=i+1;k<=n+1;k++)
					a[j][k]-=tmp*a[i][k];
			}
		//Print(n);
	}
	for(int i=1;i<=n;i++)
		a[i][n+1]/=a[i][i];
	//Print(n);
	return ;
}
double E[MAXN*MAXN+5];
int U[MAXN*MAXN+5],V[MAXN*MAXN+5];
int main(){
	int n=read(),m=read();
	for(int i=1;i<=m;i++){
		U[i]=read(),V[i]=read();
		de[U[i]]++,de[V[i]]++;
		G[U[i]].push_back(V[i]),G[V[i]].push_back(U[i]);
	}
	for(int u=1;u<n;u++){
		a[u][u]=1;
		for(int i=0;i<(int)G[u].size();i++){
			int v=G[u][i];
			if(v!=n)
				a[u][v]=-1.0/de[v];
		}
	}
	a[1][n]=1;
	Guass(n-1);
	for(int i=1;i<=m;i++)
		E[i]=a[U[i]][n]/de[U[i]]+a[V[i]][n]/de[V[i]];
	sort(E+1,E+m+1);
	double ans=0;
	for(int i=1;i<=m;i++)
		ans+=(m-i+1)*E[i];
	printf("%.3lf\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/104214964