P3232 [HNOI2013]游走

\(\color{#0066ff}{ 题目描述 }\)

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

\(\color{#0066ff}{输入格式}\)

第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1<=u,v<=N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N<=10,100%的数据满足2<=N<=500且是一个无向简单连通图。

\(\color{#0066ff}{输出格式}\)

仅包含一个实数,表示最小的期望值,保留3位小数。

\(\color{#0066ff}{输入样例}\)

3 3
2 3
1 2
1 3

\(\color{#0066ff}{输出样例}\)

3.333

\(\color{#0066ff}{数据范围与提示}\)

边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。

\(\color{#0066ff}{题解}\)

根据期望等于概率*权值,权值我们到时候分配, 先考虑概率

直接考虑边的概率我们不好分析,但如果我们知道点的概率

\(f[i]\)为从1走到i的概率,那么显然一条边\(x- y\)的概率就是\(\frac{f[x]}{du[x]}+\frac{f[y]}{du[y]}\)

考虑f怎么弄出来

其实就是跟当前点直接相连的所有点的\(f\)值*那个点的\(\frac 1 {du}\)

而且,1的f值在最后要+1, 因为刚开始就在1

所以这个东西可以用高斯消元求解, 然后转到对边的贡献

注意1和n要特判!!

下面跑一下样例

\(f[1]=\frac 1 2 f[2]+1\)

\(f[2]=\frac 1 2 f[1]\)(到了n便不能再走)

\(f[3]=\frac 1 2 f[2]+\frac 1 2 f[1]\)

通过高斯消元,解出来

\(f[1]=\frac 4 3 ,f[2]=\frac 2 3, f[3]=1\)

\(边1-2,ans=\frac 1 2 f[1]+\frac 1 2 f[2]=1\)

\(边1-3,ans=\frac 1 2 f[1]=\frac 2 3\)

\(边2-3,ans=\frac 1 2 f[2]=\frac 1 3\)

于是\(ans=\frac 1 3 * 3 + \frac 2 3 * 2 + 1 * 1=\frac 4 3 + 2=3.333\)

#include<bits/stdc++.h>
#define LL long long
LL in() {
	char ch; LL x = 0, f = 1;
	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
	return x * f;
}
const double eps = 1e-10;
const int maxn = 520;
struct node {
	int to;
	node *nxt;
	node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
double a[maxn][maxn], ans[maxn];
int n, m, du[maxn];
std::priority_queue<double> q;
std::vector<std::pair<int, int> > v;
void add(int from, int to) {
	head[from] = new node(to, head[from]);
}
void gaosi() {
	for(int i = 1; i <= n; i++) {
		int pos = i;
		for(int j = i; j <= n; j++) if(fabs(a[j][i]) - fabs(a[pos][i]) >= eps) pos = j;
		if(pos != i) for(int j = 1; j <= n + 1; j++) std::swap(a[pos][j], a[i][j]);
		for(int j = i + 1; j <= n + 1; j++) a[i][j] /= a[i][i];
		a[i][i] = 1;
		for(int j = 1; j <= n; j++) {
			if(i == j) continue;
			double now = a[j][i];
			for(int k = i; k <= n + 1; k++) a[j][k] -= now * a[i][k];
		}
	}
	for(int i = 1; i <= n; i++) ans[i] = a[i][n + 1];
}
int main() {
	n = in(), m = in();
	int x, y;
	for(int i = 1; i <= m; i++) {
		du[x = in()]++, du[y = in()]++;
		add(x, y), add(y, x);
		v.push_back(std::make_pair(x, y));
	}
	a[1][n + 1] -= 1;
	for(int i = 1; i <= n; i++) {
		a[i][i] -= 1;
		for(node *o = head[i]; o; o = o->nxt) {
			if(o->to == n) continue;
			a[i][o->to] += 1.0 / (double)du[o->to];
		}
	}
	gaosi();
	for(int i = 0; i < (int)v.size(); i++) {
		double now = 0;
		x = v[i].first, y = v[i].second;
		if(x != n) now += 1.0 / (double)du[x] * ans[x];
		if(y != n) now += 1.0 / (double)du[y] * ans[y];
		q.push(now);
	}
	double tot = 0;
	for(int i = 1; i <= m; i++) tot += (double)i * q.top(), q.pop();
	printf("%.3f", tot);
	return 0;
}
posted @ 2019-02-21 20:19  olinr  阅读(304)  评论(0编辑  收藏  举报