程序设计思维 week7 作业B-Airport Express

题目

今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!

Input

输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。

Ouput

对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行。

Sample Input

4 1 4
4
1 2 2
1 3 3
2 4 4
3 4 5
1
2 4 3

Sample Ouput

1 2 4
2
5

思路

本题给定了起点和终点,且商业线最多乘坐一次(故可以枚举每一条商业线)。枚举每一条商业线(u v w),计算起点到u的最短路以及v到终点的最短路再加上该商业线花费的时间w。
使用dijkstra求没有负边的单源最短路。
起点为源点求单源最短路,得dis1[];以终点为源点求单源最短路,得dis2[]。枚举商业线(u,v,w),取min{dis1[u]+dis2[v]+w, dis1[v]+dis2[u]+w},得到走商业线的最短路。再与不走商业线的最短路比较,取较小的。

Dijkstra

用于解决图中没有负边单源最短路问题。

复杂度O((n+m)logn)。

实现流程:

  • 设s为源点,dis[a]表示源点s到点a的最短路径,初始化dis[s]=0,dis[i]=inf,将s加入最小堆
  • 每次从堆中取出堆顶x,遍历x的所有临界边(x y w),比较dis[y]与dis[x]+w的大小。——松弛操作
  • dis[y]>dis[x]+w,则松弛成功。更新dis[y]的大小,将y加入最小堆
  • 不断执行上述操作,直到最小堆为空,最后得到的dis数组记为所求单源最短路值。

由于图中只有正边,因此每个点只会被最小堆弹出一次,即一旦某个点被最小堆弹出,则不会再被松弛,dis的值即为最短路。

代码

#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;
struct Edge{
    int to,w,next=-1;
}edge[2005];
const int inf=1e8;
int n,s,e,m,k,head[505],tot,dis1[505],dis2[505],path1[505],path2[505];
bool vis[505];
priority_queue<pair<int,int>> q;

void add(int x,int y,int w){
    edge[++tot].to=y;
    edge[tot].next=head[x];
    edge[tot].w=w;
    head[x]=tot;
}

void dijkstra(int s,int *dis,int *path){
    while(q.size())
        q.pop();
    memset(vis, false, sizeof(vis));
    path[s]=-1;dis[s]=0;
    q.push(make_pair(0, s));
    while(q.size()){
        int x=q.top().second;
        q.pop();
        if(vis[x])//访问过,取下一个点
            continue;
        vis[x]=true;
        for(int i=head[x];i!=-1;i=edge[i].next){
            int y=edge[i].to,w=edge[i].w;
            if(dis[y]>dis[x]+w){
                dis[y]=dis[x]+w;
                q.push(make_pair(-dis[y], y));
                path[y]=x;
            }
        }
    }
}

int main() {
    bool firstInput=true;
    while(scanf("%d%d%d",&n,&s,&e)!=EOF){
        for(int i=1;i<=n;i++){
            dis1[i]=inf;
            dis2[i]=inf;
            path1[i]=-1;
            path2[i]=-1;
            head[i]=-1;
        }
        tot=0;
        scanf("%d",&m);
        for(int i=0;i<m;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            add(x, y, z);
            add(y, x, z);
        }
        dijkstra(s, dis1, path1);
        dijkstra(e, dis2, path2);
        int u,v,ans=inf;
        scanf("%d",&k);
        for(int i=0;i<k;i++){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(dis1[x]+dis2[y]+z<ans){
                u=x;v=y;
                ans=dis1[x]+dis2[y]+z;
            }
            if(dis1[y]+dis2[x]+z<ans){
                u=y;v=x;
                ans=dis1[y]+dis2[x]+z;
            }
        }
        if(firstInput)
            firstInput=false;
        else
            printf("\n");
        vector<int>vec;
        if(dis1[e]<ans){//不使用商业线
            for(int i=e;i!=s;i=path1[i])
                vec.push_back(i);
            vec.push_back(s);
            for(int i=(int)vec.size()-1;i>0;i--)
                printf("%d ",vec[i]);
            printf("%d\nTicket Not Used\n%d\n",vec[0],dis1[e]);
        }
        else{//使用商业线
            for(int i=u;i!=s;i=path1[i])
                vec.push_back(i);
            vec.push_back(s);
            for(int i=(int)vec.size()-1;i>=0;i--)
                printf("%d ",vec[i]);
            for(int i=v;i!=e;i=path2[i])
                printf("%d ",i);
            printf("%d\n",e);
            printf("%d\n%d\n",u,ans);
        }
    }
}

总结

这道题一开始在main函数中memset(vis, false, sizeof(vis)),还忘记在第二次dijkstra之前初始化了,wa了好久。
题目链接

发布了24 篇原创文章 · 获赞 2 · 访问量 435

猜你喜欢

转载自blog.csdn.net/weixin_43805228/article/details/105410058
今日推荐