zoukankan      html  css  js  c++  java
  • NYOJ 115 城市平乱 (最短路)

    * 题目链接*

    描述

    南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市。

    他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M。

    现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都派遣了一个分队沿最近路去往暴乱城市平乱。

    现在已知在任意两个城市之间的路行军所需的时间,你作为南将军麾下最厉害的程序员,请你编写一个程序来告诉南将军第一个分队到达叛乱城市所需的时间。

    注意,两个城市之间可能不只一条路。

    • 输入
      第一行输入一个整数T,表示测试数据的组数。(T<20)每组测试数据的第一行是四个整数N,M,P,Q(1<=N<=100,N<=M<=1000,M-1<=P<=100000)其中N表示部队数,M表示城市数,P表示城市之间的路的条数,Q表示发生暴乱的城市编号。随后的一行是N个整数,表示部队所在城市的编号。再之后的P行,每行有三个正整数,a,b,t(1<=a,b<=M,1<=t<=100),表示a,b之间的路如果行军需要用时为t数据保证暴乱的城市是可达的。
    • 输出
      对于每组测试数据,输出第一支部队到达叛乱城市时的时间。每组输出占一行
    • 样例输入
      1
      3 8 9 8
      1 2 3
      1 2 1
      2 3 2
      1 4 2
      2 5 3
      3 6 2
      4 7 1
      5 7 3
      5 8 2
      6 8 2
    • 样例输出
      4

    分析:

    这是一道求解最短路的问题,因为图中的权值没有涉及到负权值的情况,所以用迪杰斯特拉和spfa都能写(如果有负权值的话,迪杰斯特拉就不能用了,只能用spfa)。

    说一下迪杰斯特拉和spfa的一些小区别吧,在存储图的时候呢,迪杰斯特拉是用邻接矩阵的方式,而spfa则是用邻接表的方式,这样就能够看出来在访问与某一个点相连的路径的时候(如果数据范围比较大的话),spfa要比迪杰斯特拉快的多,事实上也是如此,还有需要说明的一点就是能用迪杰斯特拉写的题全部都能用spfa写。

    然后具体的看一下迪杰斯特拉和spfa算法:

    迪杰斯特拉算法:

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<vector>
    #include<queue>
    using namespace std;
    #define INF 0x3f3f3f3f
    int Tu[1002][1002];///存储图的信息
    int dis[1002];///到源点的最短距离
    int bj[1002];///标记这个点有没有访问过
    int jun[1002];///标记军队所在的城市
    int N,M,P,Q;
    void init()///数据的初始化
    {
        memset(jun,0,sizeof(jun));
        for(int i=1; i<=M; i++)
            for(int j=1; j<=M; j++)
            {
                if(i==j)
                    Tu[i][j]==0;
                else
                    Tu[i][j]=INF;
            }
    }
    int dij()///迪杰斯特拉算法
    {
        int sum=0;
        for(int i=1; i<=M; i++)
        {
            dis[i]=Tu[Q][i];///到i点的最短距离
            bj[i]=0;///标记这个点有没有访问过
        }
        bj[Q]=1;///1点访问过了
        int flag=Q;///下一个起始点
        int cut=1;///城市的个数
        while(cut<M)
        {
            int Min=INF;
            for(int i=1; i<=M; i++)
            {
                if(bj[i]==0&&dis[i]<Min)///找到下一个最短距离
                {
                    Min=dis[i];
                    flag=i;
                }
            }
            bj[flag]=1;///标记flag点访问过
            cut++;///城市个数加
            for(int i=1; i<=M; i++)
                if(bj[i]==0&&dis[i]>dis[flag]+Tu[flag][i])///这个点没有访问过并且最短距离可以更新
                {
                    dis[i]=dis[flag]+Tu[flag][i];
                }
        }
        int Min=INF;
        for(int i=1; i<=M; i++)///求出最小值
        {
            if(jun[i]==1)
            {
                if(dis[i]<Min)
                    Min=dis[i];
            }
        }
        return Min;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d%d",&N,&M,&P,&Q);
            init();
            int a,b,c;
            while(N--)
            {
                scanf("%d",&a);
                jun[a]=1;
            }
            while(P--)
            {
                scanf("%d%d%d",&a,&b,&c);
                Tu[a][b]=Tu[b][a]=min(Tu[a][b],c);///可能会存在重复输入路径的情况
            }
            printf("%d
    ",dij());
        }
        return 0;
    }
    

    在用邻接矩阵存储图的时候,可以动态的申请vector数组来模拟,也可以直接用数组来模拟,两种方法在思路上是完全一样的,不同之处在于时间,vector在插入元素的时候,用push_back(),在数据较多的时候是比较浪费时间的,而用数组模拟的话,有一个头插法的思想,会节省时间。

    spfa算法(vector):

     
    #include<stdio.h>
    #include<iostream>
    #include<vector>
    #include<string.h>
    #include<queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    int N,M,P,Q;
    int jun[1002];
    int Time[1002][1002];
    int dis[1002];
    int bj[1002];
    vector<int>v[1002];
    void init()
    {
        memset(jun,0,sizeof(jun));
        memset(v,0,sizeof(v));
        for(int i=0; i<=M; i++)
            for(int j=1; j<=M; j++)
            {
                if(i==j)
                    Time[i][j]=0;
                else
                    Time[i][j]=INF;
            }
    }
    int spfa()
    {
        for(int i=1; i<=M; i++)
        {
            dis[i]=INF;
            bj[i]=0;
        }
        dis[Q]=0;
        queue<int>q;
        q.push(Q);
        bj[Q]=1;
        int flag;
        while(!q.empty())
        {
            flag=q.front();
            q.pop();
            bj[flag]=0;
            for(int i=0; i<v[flag].size(); i++)
            {
                int t=v[flag][i];
                if(dis[t]>dis[flag]+Time[flag][t])
                {
                    dis[t]=dis[flag]+Time[flag][t];
                    if(bj[t]==0)
                    {
                        bj[t]=1;
                        q.push(t);
                    }
                }
            }
        }
        int Min=INF;
        for(int i=1; i<=M; i++)
        {
            //  printf("dis  %d
    ",dis[i]);
            if(jun[i]==1)
            {
                if(dis[i]<Min)
                    Min=dis[i];
            }
        }
        return Min;
    }
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d%d",&N,&M,&P,&Q);
            init();
            int a,b,c;
            while(N--)
            {
                scanf("%d",&a);
                jun[a]=1;
            }
            while(P--)
            {
                scanf("%d%d%d",&a,&b,&c);
                v[a].push_back(b);
                v[b].push_back(a);
                Time[a][b]=c;
                Time[b][a]=c;
            }
            printf("%d
    ",spfa());
        }
        return 0;
    }        
    

    spfa算法(数组):

    #include<stdio.h>
    #include<iostream>
    #include<vector>
    #include<string.h>
    #include<queue>
    #define INF 0x3f3f3f3f
    using namespace std;
    int N,M,P,Q,cnt;
    int jun[1002];///标记军队所在的城市
    int dis[1002];///到源点的距离
    int bj[1002];///标记这个点有没有访问过
    int head[1002];///头节点
    struct Node
    {
        int to;///相邻的下一个点
        int w;///权值
        int pre;///前一条边
    } node[100002];
    void add(int a,int b,int c)
    {
        node[cnt].to=b;
        node[cnt].w=c;
        node[cnt].pre=head[a];///采用头插法的思想,给他们的前一条边赋值
        head[a]=cnt;
        cnt++;
    }
    void init()///初始化
    {
        memset(jun,0,sizeof(jun));
        memset(head,-1,sizeof(head));
        memset(node,0,sizeof(node));
    }
    int spfa( )
    {
        for(int i=1; i<=M; i++)
        {
            dis[i]=INF;
            bj[i]=0;
        }
        dis[Q]=0;
        queue<int>q;
        q.push(Q);
        bj[Q]=1;
        int flag;
        while(!q.empty())
        {
            flag=q.front();
            q.pop();
            bj[flag]=0;
            for(int i=head[flag]; i!=-1; i=node[i].pre)///访问所有的与它相连的边
            {
                int t=node[i].to;
                if(dis[t]>dis[flag]+node[i].w)
                {
                    dis[t]=dis[flag]+node[i].w;
                    if(bj[t]==0)
                    {
                        bj[t]=1;
                        q.push(t);
                    }
                }
            }
        }
        int Min=INF;
        for(int i=1; i<=M; i++)
        {
            //  printf("dis  %d
    ",dis[i]);
            if(jun[i]==1)
            {
                if(dis[i]<Min)
                    Min=dis[i];
            }
        }
        return Min;
    }
    int main()
    {
    
        int T;
        scanf("%d",&T);
        while(T--)
        {
            cnt=0;
            scanf("%d%d%d%d",&N,&M,&P,&Q);
            init();
            int a,b,c;
            while(N--)
            {
                scanf("%d",&a);
                jun[a]=1;
            }
            while(P--)
            {
                scanf("%d%d%d",&a,&b,&c);
                add(a,b,c);
                add(b,a,c);
            }
            printf("%d
    ", spfa());
        }
        return 0;
    }
    
  • 相关阅读:
    linux下使用tar命令
    ContentType和@ResponseBody
    ActiveMQ两种模式PTP和PUB/SUB<转>
    Postgresql分表与优化
    PostgreSQL存储过程<转>
    PostgreSQL Table Partitioning<转>
    Postgresql查询表的大小
    Postgresql添加/删除触发器示例
    Android TextView 支持的HTML标签
    我只是一直很努力
  • 原文地址:https://www.cnblogs.com/cmmdc/p/6772421.html
Copyright © 2011-2022 走看看