CF208C

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值