P3232 [HNOI2013]游走 [高斯消元]
题目描述见链接 .
题目要求走过的 边 编号和期望最小,
先求出 点 的期望值 , 走过一条边 的期望值即为 , 最后将期望值大的边赋予较小的编号即可 .
现在求 点 的期望值, , 依此可以列出 个方程, 高斯消元 解方程即可 .
- 与 号点相连的 点, 边 不用计 的贡献 .
- 走到 的实际期望值 为解出的期望 .
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 505;
const double eps = 1e-14;
int N;
int M;
int num0;
int du[maxn];
int head[maxn];
int u[maxn*maxn];
int v[maxn*maxn];
double B[maxn*maxn];
double A[maxn][maxn];
struct Edge{ int nxt, to; } edge[maxn*maxn<<1];
void Add(int from, int to){
edge[++ num0] = (Edge){ head[from], to };
head[from] = num0; du[from] ++;
}
void Guass(){
for(reg int i = 1; i < N; i ++){
int max_id = i;
for(reg int j = i+1; j < N; j ++)
if(fabs(A[j][i]) > fabs(A[max_id][i])) max_id = j;
std::swap(A[max_id], A[i]);
for(reg int j = N; j >= i; j --) A[i][j] /= A[i][i];
for(reg int j = i+1; j < N; j ++){
if(fabs(A[j][i]) < eps) continue ;
for(reg int k = N; k >= i; k --) A[j][k] -= A[j][i] * A[i][k];
}
}
for(reg int i = N-1; i >= 1; i --)
for(reg int j = i+1; j < N; j ++) A[i][N] -= A[j][N]*A[i][j];
}
int main(){
N = read(), M = read();
for(reg int i = 1; i <= M; i ++){
u[i] = read(), v[i] = read();
Add(u[i], v[i]), Add(v[i], u[i]);
}
for(reg int i = 1; i < N; i ++){
A[i][i] = 1;
for(reg int j = head[i]; j; j = edge[j].nxt){
int to = edge[j].to;
if(to == N) continue ;
A[i][to] = -1.0/du[to];
}
}
A[1][N] = 1;
Guass();
for(reg int i = 1; i <= M; i ++){
if(u[i] != N) B[i] += A[u[i]][N]/du[u[i]];
if(v[i] != N) B[i] += A[v[i]][N]/du[v[i]];
}
std::sort(B+1, B+M+1);
double Ans = 0;
for(reg int i = 1; i <= M; i ++) Ans += B[i] * (M-i+1);
printf("%.3lf\n", Ans);
return 0;
}