(color{#0066ff}{ 题目描述 })
一个无向连通图,顶点从1编号到N,边从1编号到M。 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小。
(color{#0066ff}{输入格式})
第一行是正整数N和M,分别表示该图的顶点数 和边数,接下来M行每行是整数u,v(1<=u,v<=N),表示顶点u与顶点v之间存在一条边。 输入保证30%的数据满足N<=10,100%的数据满足2<=N<=500且是一个无向简单连通图。
(color{#0066ff}{输出格式})
仅包含一个实数,表示最小的期望值,保留3位小数。
(color{#0066ff}{输入样例})
3 3
2 3
1 2
1 3
(color{#0066ff}{输出样例})
3.333
(color{#0066ff}{数据范围与提示})
边(1,2)编号为1,边(1,3)编号2,边(2,3)编号为3。
(color{#0066ff}{题解})
根据期望等于概率*权值,权值我们到时候分配, 先考虑概率
直接考虑边的概率我们不好分析,但如果我们知道点的概率
设(f[i])为从1走到i的概率,那么显然一条边(x- y)的概率就是(frac{f[x]}{du[x]}+frac{f[y]}{du[y]})
考虑f怎么弄出来
其实就是跟当前点直接相连的所有点的(f)值*那个点的(frac 1 {du})
而且,1的f值在最后要+1, 因为刚开始就在1
所以这个东西可以用高斯消元求解, 然后转到对边的贡献
注意1和n要特判!!
下面跑一下样例
(f[1]=frac 1 2 f[2]+1)
(f[2]=frac 1 2 f[1])(到了n便不能再走)
(f[3]=frac 1 2 f[2]+frac 1 2 f[1])
通过高斯消元,解出来
(f[1]=frac 4 3 ,f[2]=frac 2 3, f[3]=1)
(边1-2,ans=frac 1 2 f[1]+frac 1 2 f[2]=1)
(边1-3,ans=frac 1 2 f[1]=frac 2 3)
(边2-3,ans=frac 1 2 f[2]=frac 1 3)
于是(ans=frac 1 3 * 3 + frac 2 3 * 2 + 1 * 1=frac 4 3 + 2=3.333)
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const double eps = 1e-10;
const int maxn = 520;
struct node {
int to;
node *nxt;
node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
double a[maxn][maxn], ans[maxn];
int n, m, du[maxn];
std::priority_queue<double> q;
std::vector<std::pair<int, int> > v;
void add(int from, int to) {
head[from] = new node(to, head[from]);
}
void gaosi() {
for(int i = 1; i <= n; i++) {
int pos = i;
for(int j = i; j <= n; j++) if(fabs(a[j][i]) - fabs(a[pos][i]) >= eps) pos = j;
if(pos != i) for(int j = 1; j <= n + 1; j++) std::swap(a[pos][j], a[i][j]);
for(int j = i + 1; j <= n + 1; j++) a[i][j] /= a[i][i];
a[i][i] = 1;
for(int j = 1; j <= n; j++) {
if(i == j) continue;
double now = a[j][i];
for(int k = i; k <= n + 1; k++) a[j][k] -= now * a[i][k];
}
}
for(int i = 1; i <= n; i++) ans[i] = a[i][n + 1];
}
int main() {
n = in(), m = in();
int x, y;
for(int i = 1; i <= m; i++) {
du[x = in()]++, du[y = in()]++;
add(x, y), add(y, x);
v.push_back(std::make_pair(x, y));
}
a[1][n + 1] -= 1;
for(int i = 1; i <= n; i++) {
a[i][i] -= 1;
for(node *o = head[i]; o; o = o->nxt) {
if(o->to == n) continue;
a[i][o->to] += 1.0 / (double)du[o->to];
}
}
gaosi();
for(int i = 0; i < (int)v.size(); i++) {
double now = 0;
x = v[i].first, y = v[i].second;
if(x != n) now += 1.0 / (double)du[x] * ans[x];
if(y != n) now += 1.0 / (double)du[y] * ans[y];
q.push(now);
}
double tot = 0;
for(int i = 1; i <= m; i++) tot += (double)i * q.top(), q.pop();
printf("%.3f", tot);
return 0;
}