题外话:
小Z,你的袜子找到了吗?
分析:
题目要求使得小Z获得的总分的期望值最小
那么考虑我们知道那些算法可以达到这个目的:
二分(不可能),线性规划(有门,但是不等式呢),dp(有点靠谱)
但是我们的目的是确定边的编号,dp之类的应该是没有办法的
期望=概率*权值
想要期望小,实际上就是 “概率*权值” 尽量小
显然,我们希望概率越小的边权值越大(mmp又是一个贪心。。。)
我们可以对于每一条边计算经过ta的概率:
f(w(x,y))=(x=n)?0:f(w(u,x))∗out[w(u,x)]+(y=n)?0:f(w(y,v))∗out[w(y,v)]
f
(
w
(
x
,
y
)
)
=
(
x
=
n
)
?
0
:
f
(
w
(
u
,
x
)
)
∗
o
u
t
[
w
(
u
,
x
)
]
+
(
y
=
n
)
?
0
:
f
(
w
(
y
,
v
)
)
∗
o
u
t
[
w
(
y
,
v
)
]
out[w]
o
u
t
[
w
]
表示从边w出来的概率
注意:x,y不能是n,因为到达n后游走就结束了,必然不会再出来了
直接求解边的概率列方程比较复杂
所以我们换一个思路,先解出到达每个点的概率,那么边的概率就可以算出来了
对于每一个点(除了n号点,到达n后游走就结束了):
g(x)=∑g(y)deg(y)
g
(
x
)
=
∑
g
(
y
)
d
e
g
(
y
)
(存在边w(x,y))
g(x)−∑g(y)deg(y)=0
g
(
x
)
−
∑
g
(
y
)
d
e
g
(
y
)
=
0
我们从1出发的,所以1号点的初始概率是1,但是这并不是最后的概率
因为起点是可以重复经过的,所以还会有再次到达的概率
那么在
f(w(x,y))
f
(
w
(
x
,
y
)
)
中:
f(w(u,x))∗out[w(u,x)]=g(x)dep(x)
f
(
w
(
u
,
x
)
)
∗
o
u
t
[
w
(
u
,
x
)
]
=
g
(
x
)
d
e
p
(
x
)
f(w(y,v))∗out[w(y,v)]=g(y)dep(y)
f
(
w
(
y
,
v
)
)
∗
o
u
t
[
w
(
y
,
v
)
]
=
g
(
y
)
d
e
p
(
y
)
(我们先要到达
w(x,y)
w
(
x
,
y
)
的一个端点,而从这个端点进入
w(x,y)
w
(
x
,
y
)
还有一定的概率)
我们就可以用高斯消元求解g了,再计算f
之后从小到大安排编号即可
注意:
- 一号结点的初始概率为1,所以a[1][n+1]=-1
tip
其实还是有一点疑问:为什么概率要用高斯消元(求解未知量)呢?
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-7;
const int N=502;
struct node{
int x,y;
};
node e[150000];
int n,m,du[N];
double a[N][N],f[150000];
void gauss() {
int now=1,to;
for (int i=1;i<=n;i++) {
for (to=now;to<=n;to++)
if (fabs(a[to][i])>eps) break;
if (to>n) continue;
if (to!=now)
for (int j=1;j<=n+1;j++)
swap(a[now][j],a[to][j]);
for (int j=1;j<=n;j++)
if (j!=now) {
double t=a[j][i]/a[now][i];
for (int k=1;k<=n+1;k++)
a[j][k]-=t*a[now][k];
}
now++;
}
for (int i=1;i<=n;i++) a[i][n+1]/=a[i][i];
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++) {
scanf("%d%d",&e[i].x,&e[i].y);
du[e[i].x]++; du[e[i].y]++;
}
for (int i=1;i<=n;i++) a[i][i]=1.0;
a[1][n+1]=1.0; //初始概率为1
for (int i=1;i<=m;i++) {
int x=e[i].x,y=e[i].y;
if (y!=n) a[x][y]-=1.0/(double)du[y]; //不是n结点
if (x!=n) a[y][x]-=1.0/(double)du[x];
}
gauss();
for (int i=1;i<=m;i++) {
int x=e[i].x;
int y=e[i].y;
if (x!=n) f[i]+=a[x][n+1]/(double)du[x];
if (y!=n) f[i]+=a[y][n+1]/(double)du[y];
}
sort(f+1,f+1+m);
double ans=0;
for (int i=1;i<=m;i++)
ans+=f[i]*(double)(m-i+1);
printf("%0.3lf",ans);
return 0;
}