游走
游走
高斯消元 + 期望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;
}