zoukankan      html  css  js  c++  java
  • HAOI 2005 路由选择问题 (最短路+次短路)

    问题描述

    X城有一个含有N个节点的通信网络,在通信中,我们往往关心信息从一个节点I传输到节点J的最短路径。遗憾的是,由于种种原因,线路中总有一些节点会出故障,因此在传输中要避开故障节点。
    任务一:在己知故障节点的情况下,求避开这些故障节点,从节点I到节点J的最短路径S0。
    任务二:在不考虑故障节点的情况下,求从节点I到节点J的最短路径S1、第二最短路径S2。

    输入文件

    第1行: N I J (节点个数 起始节点 目标节点)
    第2—N+1行: Sk1 Sk2…SkN (节点K到节点J的距离为SkJ K=1,2,……,N)
    最后一行: P T1 T2……Tp (故障节点的个数及编号)

    输出文件

    S0 S1 S2 (S1<=S2 从节点I到节点J至少有两条不同路径)

    样例输入

    5 1 5
    0 10 5 0 0
    10 0 0 6 20
    5 0 0 30 35
    0 6 30 0 6
    0 20 35 6 0
    1 2

    样例输出
    40 22 30

    约束条件

    (1)N<=50 N个节点的编号为1,2,…,N
    (2)Skj为整数,Skj<=100,(K,J=1,2…,N 若Skj=0表示节点K到节点J没线路)
    (3)P<=5  

    思路:

    首先简单的说一下次短路经:

    次短路径可以看作是k短路径问题的一种特殊情况,求k短路径有Yen算法等较为复杂的方法,对于次短路径,可以有更为简易的方法。下面介绍一种求两个顶点之间次短路径的解法。

    我们要对一个有向赋权图(无向图每条边可以看作两条相反的有向边)的顶点S到T之间求次短路径,首先应求出S的单源最短路径。遍历有向图,标记出可以在最短路径上的边,加入集合K。然后枚举删除集合K中每条边,求从S到T的最短路径,记录每次求出的路径长度值,其最小值就是次短路径的长度。

    在这里我们以为次短路径长度可以等于最短路径长度,如果想等,也可以看作是从S到T有不止一条最短路径。如果我们规定求从S到T大于最短路径长度的次短路径,则答案就是每次删边后大于原最短路径的S到T的最短路径长度的最小值。

    用Dijkstra+堆求单源最短路径,则每次求最短路径时间复杂度为O(Nlog(N+M) + M),所以总的时间复杂度为O(NM*log(N+M) + M^2)。该估计是较为悲观的,因为一般来说,在最短路径上的边的条数要远远小于M,所以实际效果要比预想的好。

    对于前面两个询问,直接spfa即可(同时记录最短路径),对于第三个询问,每一次删掉最短路径中的一条边,然后再spfa找到删过边后的最短路,即为次短路。

    代码:

    #include<stdio.h>
    #include<iostream>
    #include<queue>
    #include<string.h>
    const int INF=0x3f3f3f3f;
    using namespace std;
    int n,st,ed,cnt,st1,ed1;
    struct Node
    {
        int to,val;
        int Next;
    }node[2600];
    int head[55];//头结点
    int pro[55];//有问题的点
    int pre[55];//前去节点
    int dis[55];//距离
    int vis[55];//标记有没有访问过
    void init()
    {
        for(int i=0;i<=n;i++)
            head[i]=-1;
    }
    void add(int a,int b,int w)
    {
        node[cnt].to=b;
        node[cnt].val=w;
        node[cnt].Next=head[a];
        head[a]=cnt;
        cnt++;
    }
    void spfa(int flag)
    {
        if(flag==1)
            memset(pre,0,sizeof(pre));
        queue<int>q;
        for(int i=1;i<=n;i++)
        {
            dis[i]=INF;
            vis[i]=0;
        }
        dis[st]=0;
        vis[st]=1;
        pre[st]=-1;
        q.push(st);
    
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            vis[x]=0;
            for(int i=head[x];i!=-1;i=node[i].Next)
            {
                int y=node[i].to;
                if ((x == st1 && y == ed1) || (x == ed1 && y == st1)) continue;
                if(pro[y]==1) continue;//第一次找最短路的时候遇见故障点要跳过
                if(dis[y]>dis[x]+node[i].val)
                {
                    dis[y]=dis[x]+node[i].val;
                    if(flag==1)  pre[y]=x;
                    if(vis[y]==0)
                    {
                        vis[y]=1;
                        q.push(y);
                    }
                }
            }
        }
    }
    int main()
    {
        while(scanf("%d%d%d",&n,&st,&ed))
        {
            init();
            cnt=0;
            int w,num;
            for(int i=1; i<=n; i++)
                for(int j=1; j<=n; j++)
                {
                    scanf("%d",&w);
                    if(w!=0)
                    {
                        add(i,j,w);//按照头插法保存下图的信息
                    }
                }
            scanf("%d",&w);
            for(int i=1;i<=w;i++)//将有问题的点标记下来
            {
                scanf("%d",&num);
                pro[num]=1;
            }
    
            //去掉有故障的点寻找最短路
            spfa(1);
            printf("%d ",dis[ed]);
    
            //不考虑有故障的点寻找最短路
            memset(pro,0,sizeof(pro));
            spfa(1);
            printf("%d ",dis[ed]);
    
            //求次短路是在不考虑有故障的点的基础的
            int Min=INF;
            int v ;
            v=ed;//倒着一条边一条边的删除
            while(pre[v]!= -1)
            {
                st1=pre[v];//删除的这条边的起始点
                ed1=v;//删除的这条边的终点
                spfa(0);
                Min=min(Min,dis[ed]);//然后在这些最短路里面找一条最小的
                v=pre[v];
            }
             printf("%d
    ",Min);
        }
        return 0;
    }
    
  • 相关阅读:
    C#操作Word完全功略
    Ubuntu安装BackExec Remote Agent for Linux
    curl快速实现网速测试
    GitHub已将持续集成服务器Janky开源
    串行(Sequential)、并发(Concurrent)、并行(parallel)与分布式(distributed)
    使用ld的wrap选项替换已有库函数
    Linux获取程序编译参数
    配置Apache+Tomcat实现SSO(单点登录)
    ipad+Blackberry构建临时网络访问
    巧解 JavaScript 中的嵌套替换
  • 原文地址:https://www.cnblogs.com/cmmdc/p/7725423.html
Copyright © 2011-2022 走看看