3143: [Hnoi2013]游走
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3110 Solved: 1365
[ Submit][ Status][ Discuss]
Description
一个无向连通图,顶点从1编号到N,边从1编号到M。
小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。
现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
Input
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1≤u,v≤N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N≤10,100%的数据满足2≤N≤500且是一个无向简单连通图。
Output
仅包含一个实数,表示最小的期望值,保留3位小数。
Sample Input
3 3
2 3
1 2
1 3
2 3
1 2
1 3
Sample Output
3.333
HINT
边
(1,2)
编号为
1
,边
(1,3)
编号
2
,边
(2,3)
编号为
3
。
Source
这道题看起来好像是简单的逆拓扑序求期望, 事实上并不行, 因为他是无向图.
我们先想一下题目中所要求的东西, 可以想到一个简单的贪心思路, 那就是算出每条边的期望经过次数, 然后排序, 把期望经过次数少的分大编号, 这样肯定是最优的.
那么问题就转化成了求边的期望经过次数. 边的期望次数可以用点的期望次数来算. 可以发现是一个n行n列的方程, 直接高斯消元即可.
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-10;
const int maxn = 505;
const int maxm = 250005;
int st[maxm], ed[maxm], d[maxn], h[maxn], cnt, num, n, m;
double a[maxn][maxn], f[maxm], w[maxm], ans;
struct edge{int nxt, v;}e[maxm * 2];
inline const int read(){
register int f = 1, x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') x = (x<<3) + (x<<1) + ch - '0', ch = getchar();
return f * x;
}
inline void add(int u, int v){
e[++num].v = v, e[num].nxt = h[u], h[u] = num;
e[++num].v = u, e[num].nxt = h[v], h[v] = num;
}
inline void Gauss(){
for(int i = 1; i <= n; ++i){
int j = -1;
for(int k = cnt + 1; k <= n; ++k)
if(fabs(a[k][i]) > fabs(a[j][i])) j = k;
if(j == -1) continue;
for(int k = i; k <= n + 1; ++k) swap(a[j][k], a[cnt + 1][k]);
for(j = 1; j <= n; ++j){
if(fabs(a[j][i]) < eps || j == cnt + 1) continue;
double r = a[j][i] / a[cnt + 1][i];
for(int k = i; k <= n + 1; ++k)
a[j][k] -= r * a[cnt + 1][k];
}
++cnt;
}
for(int i = 1; i <= n; ++i) f[i] = a[i][n + 1] / a[i][i];
}
int main(){
n = read(), m = read(), --n;
for(int i = 1; i <= m; ++i){
st[i] = read(), ed[i] = read();
add(st[i], ed[i]), d[st[i]]++,d[ed[i]]++;
}
for(int u = 1; u <= n; ++u){
for(int i = h[u]; i; i = e[i].nxt)
if(e[i].v != n + 1) a[u][e[i].v] += (double) 1.0 / d[e[i].v];
a[u][u] = -1;
}
a[1][n+1]=-1;
Gauss();
for(int i = 1; i <= m; ++i) w[i] = (double) f[st[i]] / d[st[i]] + (double) f[ed[i]] / d[ed[i]];
sort(w + 1, w + m + 1);
for(int i = 1; i <= m; ++i) ans += (m - i + 1) * w[i];
printf("%0.3lf\n", ans);
}