「HNOI2013」游走
## [「HNOI2013」游走 ](https://loj.ac/problem/2383)
### 解题思路 :
```cpp /*program by mangoyang*/ #include
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define par pair
#define mp make_pair
#define fi first
#define se second
const int N = 1005, M = 1000005;
map s;
double buf[M];
vector g[N];
int deg[N], a[M], n, m, col;
typedef double Matrix[N][N];
Matrix A;
inline bool cmp(int x, int y){ return buf[x] > buf[y]; }
inline void Gauss(Matrix &A){
for(int i = 1; i <= n; i++){
int r = i;
for(int j = i + 1; j <= n; j++)
if(fabs(A[j][i]) > fabs(A[r][i])) r = j;
for(int j = i; j <= n + 1; j++) swap(A[i][j], A[r][j]);
for(int j = i + 1; j <= n; j++){
double f = A[j][i] / A[i][i];
for(int k = i; k <= n + 1; k++) A[j][k] -= A[i][k] * f;
}
}
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];
A[i][n+1] /= A[i][i];
}
}
int main(){
read(n), read(m);
for(int i = 1, x, y; i <= m; i++){
read(x), read(y);
deg[x]++, g[x].push_back(y);
deg[y]++, g[y].push_back(x);
par now = mp(Min(x, y), Max(x, y));
if(!s[now]) s[now] = ++col;
}
for(int i = 1; i <= n; i++){
for(auto v : g[i]) if(v != n)
A[i][v] = 1.0 / (double) deg[v];
A[i][i] = -1;
A[i][n+1] = i == 1 ? -1 : 0;
}
Gauss(A);
for(int i = 1; i < n; i++){
double val = A[i][n+1];
for(auto v : g[i]){
int c = s[mp(Min(i, v), Max(i, v))];
buf[c] += val / (double) deg[i];
}
}
for(int i = 1; i <= m; i++) a[i] = i;
sort(a + 1, a + m + 1, cmp);
double ans = 0;
for(int i = 1; i <= m; i++) ans += (double) i * buf[a[i]];
printf("%.3lf", ans);
return 0;
}
```
题目描述
一个无向连通图,顶点从 \(1\) 编号到 \(N\) ,边从 \(1\) 编号到 \(M\) 。小 \(Z\) 在该图上进行随机游走,初始时小 \(Z\) 在 \(1\) 号顶点,每一步小 \(Z\) 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 \(Z\) 到达 \(N\) 号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这 \(M\) 条边进行编号,使得小 \(Z\) 获得的总分的期望值最小。
\(N \leq 500\)
### 解题思路 :
考虑如果能算出每一条边期望的被经过次数,那么对此从大到小排序分配编号肯定最优.
观察发现,问题可以转化为求出每个点期望的被经过次数,然后分配到每一条边上
设 \(f[i]\) 表示点 \(i\) 期望的被经过次数,显然有 \(f[i] = \sum_{haveEdge(i,v)} \frac{f[v]}{deg_v}\) 其中 \(deg_v\) 表示点 \(v\) 的度数
考虑这是一张图,这样的转移并不满足子结构,不一定能找到合适的顺序进行 \(dp\) 处理
但是考虑到 \(N\) 比较小,每一个点的转移可以看做一个方程,所有点形成一个方程组,直接高斯消元求解即可
注意要从点 \(1\) 出发,点 \(N\) 结束,所以点 \(1\) 推出的方程要额外加上 \(1\) 的次数,点 \(N\) 不能作为转移到别人的点,不能加进方程组里
```cpp /*program by mangoyang*/ #include