程序设计思维 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了好久。
题目链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在第4周的可重复研究项目中,我将继续探索如何使用开放源代码工具和技术来实现可重复性和透明度。 首先,我将继续使用版本控制系统(如Git),以便跟踪我研究项目中的所有更改和改进。这将确保我能够回溯到每个版本的数据和代码,并对项目进行可重复性验证。在本周内,我还将学习更多关于Git分支和合并的知识,以便更好地组织和管理我的项目。 另外,我还将使用Jupyter Notebook来记录我的实验过程和结果。Jupyter Notebook提供了一个互动环境,可以将代码、文档和图形化结果结合在一起,使得我的研究成果更加易于理解和重现。我会确保我的Notebook中包含了所有必要的步骤和解释,以便他人能够准确地复现我的研究。 为了进一步提高可重复性,我还将采取一些数据预处理和清洗的措施。这些措施包括去除异常值、处理缺失数据和标准化数据等。我将确保我的数据处理过程明确记录,并提供相应的代码和文档,以便他人能够按照相同的步骤进行处理。 最后,我还计划使用容器化技术(如Docker)来实现我的研究项目的可移植性。通过将我的环境和依赖项封装在一个容器中,我可以确保其他人能够在不同的计算机和操作系统上轻松地运行我的代码和分析。 综上所述,第4周的可重复研究项目将继续探索一系列工具和技术,旨在提高我的研究项目的可重复性和透明度。通过使用版本控制系统、Jupyter Notebook、数据处理和清洗措施以及容器化技术,我将确保我的研究成果可以被其他人准确地重现和验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值