208C 最短路径及其条数
题目概述:求ansa/ansm!
Ansm是从1到n最短路径的条数,ansa是所有(all)最短路径中,安全边的个数。所谓安全边是指,这条边的两个端点中,有一个是policestation。
总结:
一看到这道题,读了第一段,我列出了6个条,关于题目中描述的图,因为没有权值,我第一个想到的就是广度优先搜索求最短路径。
我开始想,怎么样能在求最短路径的同时,把最短路径的条数也求出来,后来失败了。
接下来,我的想法是,先求出最短路径长度,然后形成一个最短路径子图的矩阵,然后用dfs求出最短路径条数,显然,我这个想法没有问题,我也成功地实现了。也就是说错误不是出在这,那么一定就是ansa的求值出现了错误,但是我不知错在哪里。
分析一下我求ansa的算法:
这个算法的第一步是求经过哪一个点的最短路径最多;第二步,如果
这点不是1或n,就把经过这个点的最短路径乘以2,否则不乘。
而问题就出在了这个第一步!在从1到n的最短路径矩阵中,我所求出的是一个顶点和他相邻的顶点之间一共有几条路,然而这不等于经过这个点的最短路径条数。
例如这个图: 1 到8之间的最短路径经过这个4的点,
1 2
1 3
2 4
3 4
4 5
5 6
5 7
7 8
6 8
4的点的也是与邻近的顶点之间路径
最多的,一共是两条,但是从整体上看,经过这个黑色点的最短路径一共有4条。就错在这里!
别人的算法:
首先,是代码的部分:
#include<stdio.h>
int d[120][120];
double c[120][120];
int n,m,x,y;
double z,mx;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j)
d[i][j]=0xffff;
else
c[i][j]=1;
for(int i=0;i<m;i++)
{
scanf("%d %d",&x,&y);
d[x][y]=c[x][y]=d[y][x]=c[y][x]=1;
}
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&&k!=i)
if(d[i][j]>d[i][k]+d[k][j])
{
d[i][j]=d[i][k]+d[k][j];
c[i][j]=c[i][k]*c[k][j];
}
else if(d[i][j]==d[i][k]+d[k][j])
c[i][j]+=c[i][k]*c[k][j];
for(int i=1;i<=n;i++)
{
z=0;
for(int j=1;j<=n;j++)
if(d[i][j]==1)
if(d[1][i]+1+d[j][n]==d[1][n])
z+=c[1][i]*c[j][n];
else if(d[1][j]+1+d[i][n]==d[1][n])
z+=c[1][j]*c[i][n];
if(mx<z)
mx=z;
}
//printf("%lf %lf\n", mx, c[1][n]);
printf("%.20f",mx/c[1][n]);
return 0;
}
这个代码或者说是算法的数据结构中比较重要的就是: d[ ][ ] c[ ][ ]。
其实并不是矩阵相乘。
d[][]是用来存储每对顶点的最短路径,
c[][]是用来存储每对顶点之间最短路径的条数。
不论是d还是c都是用了递归的思想来求解。
至于求最短路径的过程使用的是Floyd,看似简单,看似不必多说,但是这个c就是从这个算法中来的。
对于F算法,如果求i和j之间的最短距离,要经过将n个点逐次进行试探,看如果经过k点后,路径是否会变短,如果变短就经过k点,那么也就是说这个k点就被加入到了从i到j的最短路径中,那么如果有这样的样例(节省时间就不画图了):
起点 终点
1 4
1 9
1 5
1 7
9 2
4 2
5 2
5 3
2 10
2 11
3 8
11 8
7 6
6 11
一共是11个点,15条边。对于1到11经过点2的有三条路径长度等于最短路径程度,如果把2加进来,那么最短路径的条数应该加上 c【1】【2】 × c【2】【11】= 3;
另外一种情况,是如果出现第12个点,并出现边 ,(1,12)和(12,11),那么从1到11的的最短路径将从3变成2,这个时候,之前的计算的最短路径就 = 2,条数就= 经过12点最短路径的条数c【1】【12】 × c【12】【11】。
这个就是递归求解!
利用F算法,在求解最短路径长度的过程中,求出我们想要的值。
这就要求对算法有相当的理解,而不是只会背算法,而是要灵活应用。
接着就是求z,也就是ansa:
这一步的思想和上面的相同,不同的是,上面求的是经过任一点之间的最短路径个数,最后得出的c【1】【n】是所求,而这一步是求从1到n的路径中,经过某一点i,的最短路径个数zi,求出最大的zi,i就是能使网络系统安全系数最高的police位置,zi就是ansa。
【注意】同一条路,在不同的最短路径中是不一样的,由此我们分析为什么最后一个for循环求出的是经过这个点的安全路径个数,并且为什么c[1][i]*c[j][n]是对的。
这个办法的最绝妙之处就是它并没有孤立地看某一个点,而是将要求的这点与其满足条件且相邻的点联系在一起。
d[i][j]=1是指点点相邻,(i,j)就是这条边,如果police在i处,那么以i为终点和起点的路均为安全的路,那么(i,j)安全,但是(i,j)这条路一共属于多少条最短路径才呢?那就是c[1][i]*c[j][n]。找到所有与i相连的满足条件的j,然后求出的每一个(i,j)所属路径的个数之和,就是安全路的个数!
208C.END