题目
分析
看到数据规模,最先想到的是Floyd,但在统计方案的地方卡住了。浏览了几篇题解,得到了一点启示。
在floyd过程中,枚举k为中转点:如果当前需要更新,那么经过k的最短路条数附初始值为两边(i-k和k-j)的条数之积(乘法原理);如果恰巧当前就是一条最短路,那么此点最短路数加上这一(i,j)的最短路数(乘法原理,类似地)。
可以边更新最短路边统计最短路个数,也可以先求出最短路,再枚举各点和起止点统计最短路数,本质上没啥区别。
floyd结束后,枚举k为当前点,枚举(i,j)为起止点,根据公式可以直接求出答案。
如果用f[i][j]记录(i,j)路径最短路数,ans[v]表示v点的答案,公式可以改写为:
I(v)=∑i≠v,j≠vf[i][v]∗f[v][j]f[i][j];f[i][j]=∑k∈Path(i,j)f[i][k]∗f[k][j]f[i][j]
I
(
v
)
=
∑
i
≠
v
,
j
≠
v
f
[
i
]
[
v
]
∗
f
[
v
]
[
j
]
f
[
i
]
[
j
]
;
f
[
i
]
[
j
]
=
∑
k
∈
P
a
t
h
(
i
,
j
)
f
[
i
]
[
k
]
∗
f
[
k
]
[
j
]
f
[
i
]
[
j
]
此题核心在于处理方案,类似于DP套DP,但又不是严格的DP。
值得注意的是,在加边的时候f要先赋初值1,否则,如果两点间的最短路就是 这一条边,那么可能floyd不会更新(因为枚举时k不与i或j相等),就造成两次统计答案是某一时刻分母可能为0……
代码
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=120,inf=5000000;
int a[maxn][maxn];
double f[maxn][maxn],ans[maxn];
int n,m;
void Floyed()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&j!=k&&i!=k)
{
if(a[i][j]<a[i][k]+a[k][j]) continue;
if(a[i][j]>a[i][k]+a[k][j])
{
a[i][j]=a[i][k]+a[k][j];
f[i][j]=f[i][k]*f[k][j];
}
else f[i][j]+=f[i][k]*f[k][j];
}
}
void DP()
{
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j&&j!=k&&i!=k&&a[i][j]==a[i][k]+a[k][j])
ans[k]+=(double)(f[i][k]*f[k][j])/f[i][j];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
a[i][j]=a[j][i]=inf;
for(int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=a[y][x]=z;
f[x][y]=f[y][x]=1;
}
Floyed();
DP();
for(int i=1;i<=n;i++)
printf("%.3lf\n",ans[i]);
return 0;
}