游走

游走

高斯消元 + 期望dp

HNOI2013]游走 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

设从第 \(i\) 个点出来的期望次数为 \(f[i]\)

\(f[u]=\sum \frac {f[v]}{deg[v]}\;\;1<u<n\)

\(f[1]=\sum \frac {f[v]}{deg[v]}+1\) (因为一开始就在 1 号点)

\(f[n]=0\) (到 0 号点就结束了)

设第 \(i\) 条边连接 \(a,b\), 则第 \(i\) 条边经过的期望次数为 \(\frac {f[a]}{deg[a]}+\frac {f[b]}{deg[b]}\)

\(f\) 可高斯消元,因为数据很小,看到数据范围也应该想到高斯消元

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 5e2 + 10;
const double eps = 1e-12;
int n, m;
vector<vector<int> > G(N);

struct Edge
{
	int fr, to;
	double cnt;
	bool operator<(const Edge &x) const
	{
		return cnt > x.cnt;
	}
}e[N * N];
void add(int u, int v)
{
	G[u].push_back(v);
}

double a[N][N];
void print()
{
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n + 1; j++)
			cout << left << setw(8) << a[i][j] << " ";
		cout << endl;
	}
	cout << endl;
}

int guess(int n)
{
	int r, c;
	for (c = 1, r = 1; c <= n; c++)
	{
		int t = r;//本轮处理r ~ n行,1 ~ r-1 行已处理,不能再变化
		for (int i = r; i <= n; i++)//找到当前列最大的数
		{
			if (fabs(a[i][c]) > fabs(a[t][c]))
				t = i;
		}
		if (fabs(a[t][c]) < eps)//如果当前列全是0,则跳过看下一列,此时只有c++, r不变,最终以r <= n来判断解的情况
			continue;
		for (int i = c; i <= n + 1; i++)//将该列最大的数所在行换到第 r 行
			swap(a[r][i], a[t][i]);
		for (int i = n + 1; i >= c; i--)//将第 r 行的第一个非零元素变为 1
			a[r][i] /= a[r][c];
		for (int i = r + 1; i <= n; i++)//将 r+1 ~ n 行的第 c 列全部变为 0
		{
			if (fabs(a[i][c]) > eps)
				for (int j = n + 1; j >= c; j--)
					a[i][j] -= a[r][j] * a[i][c];
		}
		r++;
	}
	//print();
	// if (r <= n)//若系数矩阵每行都不是全 0,r应为n+1
	// {
		// //若r <= n, 则系数矩阵的 r ~ n 行全为 0,此时若最后一列有非 0 数,则无解
		// for (int i = r; i <= n; i++)
			// if (fabs(a[i][n+1]) > eps)
				// return 2;
		// return 1;
	// }
	for (int i = n; i >= 1; i--)//若有唯一解则回带,将阶梯矩阵变为对角型矩阵
	{
		for (int j = i + 1; j <= n; j++)
			a[i][n+1] -= a[j][n+1] * a[i][j];
	}
	return 0;
}

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int u, v;
		cin >> u >> v;
		e[i] = {u, v, 0};
		add(u, v), add(v, u);
	}
	
	for (int u = 1; u < n; u++)
	{
		a[u][u] = 1.0;
		int sz = G[u].size();
		for (int i = 0; i < sz; i++)
		{
			int v = G[u][i];
			if (v != n)
				a[u][v] = -1.0 / G[v].size();
		}
			
	}
	a[n][n] = 0;
	a[1][n] = 1;
	guess(n - 1);
	for (int i = 1; i <= m; i++)
	{
		int u = e[i].fr, v = e[i].to;
		e[i].cnt = a[u][n] / G[u].size() + a[v][n] / G[v].size();
	}
	sort(e + 1, e + m + 1);
	double ans = 0;
	for (int i = 1; i <= m; i++)
		ans += i * e[i].cnt;
	printf("%.3lf\n", ans);
	return 0;
}


posted @ 2022-05-28 20:31  hzy0227  阅读(45)  评论(0编辑  收藏  举报