山东大学数据结构课程设计实验五(低风险出行系统)

前言

数据结构课程设计第五题是每一个同学都不一样的题目,因此很难参考其他同级同学的代码。

这个题应该与北航的一个低风险出行题目类似(其实要比那个简单一点)
我看过了北航的那份源代码(2019年的一个题),感觉代码并不是那么简洁,且好像有一些错误(根据他的代码来看,是用的Floyd算法,但是这个题不是简单一个最短路可以跑完的,可能是我的水平受限,没有get到他的特殊处理)。在下载他的代码进行运行时,发现并没有通过我的数据特例,所以我还是自己重新写了一份。

而且由于这个题应该是个新的原创题,所以还是打算把我的代码和思路share出来,希望同样拿到这个题的大佬学弟学妹们,如果没有思路的话,可以看看我这个最简单、暴力的思路,抛砖引玉一下。

希望不要照抄源代码,这样违背了这个课设的初衷,希望不要被分数和绩点限制了自己的创作力与原创力。

题目要点

先将题目放在这里
在这里插入图片描述
简单提炼一下这个题目要做的事情:

①生成数据

可以进行每次程序运行前随机模拟生成数据,也可以程序运行时手动输入数据,也可以由创作者(自己)给定不变数据。
这个题就是一个图,数据包括
10个及以上的节点,每个节点分配风险值(要均匀的分配)
10条以内的一号边(即飞机)
30条以内的二号边(即火车)
不限数量的三号边(即汽车)

②要给定两种最短路解法

第一种是不限时间的,只要风险最低、最终能到目的地即可,求一条简单最短路
第二种是在第一种上加一个条件,要求在限定时间内的最短路

③创立文件

建立文件来记录求得最短路的边的顺序

④模拟时间流动并与用户交互

程序需要内置时间计数器,来模拟时间流动
而且在用户每次进行键盘输入时,能够让程序接收到,并且输出当前的所在位置(比如9:00时在北京等到5号飞机,该飞机是从北京飞往上海的)

代码讲解

要求就是以上列举的那么多。
一个一个解决。
①对于数据问题
我是允许用户直接输入的,即每次开始前需要用户提供数据所在的文件位置,自动从文件中读取点、边的数据。这个地方可以参考一下当前中国的那个风险分布情况,根据实际情况填写(毕竟所有的程序都是要服务现实的)
②对于最短路解法
这个题目的关键就在于这里。
首先常见的最短路算法也可能只是我见识浅薄,只学过这几种
有Dijkstra算法(贪心算法),Floyd算法(动态规划算法),以及SPFA算法(我感觉是Dijkstra算法的变种)
首先说一下Dijkstra算法,这个算法可以用这个题吗?
不行!!!
为什么不行?因为我觉得不行 我们这个题的特殊点在于,对于一个节点,可能在这个时候某些边不能用,要做抉择,要么选择等一会用这条边,或者是直接不用这条边。那么Dijkstra的特性是当前局部贪心,这就带来一个问题,如何衡量价值,因为贪心贪的也是价值。
先考虑不限时算法
价值用时间来表示吗?不行!因为要最低风险
价值用风险来表示吗?好像也不太行!因为有特例(也是北航程序卡的数据特例)
比如只有三个节点A、B、C,都是中风险地区,一共有以下三条边
A->B 9:00----13:00
A->B 10:00----11:00
B->C 12:00----14:00
起始点为A,目标点为C。

8:00从A点出发,如果以当前点的风险值进行决策,那么就会选择9:00的边,那么到达B点时需要再额外等23小时,显然不是最优解
最优解为在A点等到10:00,乘坐10:00的火车到达C点。
所以可以看到Dijkstra算法好像没法使用至少我没有想到特殊的处理方法

那么Floyd可以吗?也不行!也可能是我动态规划学的太差,用的不好
为什么?
在这个题动态规划的好处在于,可以优化空间复杂度(对于时间复杂度要遍历所有边,和DFS暴力剪枝其实不会差太多,因为暴力剪枝可以进行优化)
考虑几种动态规划法
第一种,只记录到某点的最低风险(显然是错误的,之前的数据特例可以卡掉)
第二种,记录某点到某点的最低风险(也不行,无法保证全局最优,依旧是先前所举的数据特例)
我只想到这两种动态规划法,因此就没用Floyd,还是太菜了

那么能跑的算法是什么呢?
我这里是用的最笨、最暴力的算法,DFS暴力枚举剪枝
首先先对边进行处理,用了三个辅助处理变量来帮忙
主要解释一下clock和risk,因为是dfs所以,可以一直使用下去,所以用clock来记录当前到起始城市的时间,risk来记录风险值,可以一直传递下去。且每一次计算都是由上一条边进行的,所以只要把初始的clock归为出发时间,risk置0,就可以一直使用,不用担心中途出错的问题

//直达交通路线
struct ROUTE
{
    //车次号、起止城市、起止时间、跨天数、交通工具
    int no;
    int start_city, end_city;
    int start_time, end_time;
    int day;
    int tool;
    //下面是辅助记录用的
    //辅助记录用的是否使用过,到start城市的时间,此时携带的risk
    bool use;
    int clock;
    float risk;
    ROUTE()
    {
        tool = 3;
        clock = 0;
        use = false;
        no = -1;
        risk = 0;
    }
};

再就是对风险值数组进行处理
我是开了一个三维数组,第一维记录出发城市,第二维记录到达城市,第三维记录是通过哪一条边得到的这个最低风险(否则还是会被特例卡掉,因为要剪枝来及时终止,但是这样也带来问题,如果最优解是最后DFS遍历到的,就会导致几乎要遍历所有的可能才能得到最优解,这也是DFS暴力剪枝的弊端之一)

处理完数据之后,算法就很简单了,routing是外部调用的,route是被调用的递归函数

void graph::routing(int start, int begin, int end)
{
    if (begin == end)
    {
        route_ans = -1;
        // cout << 0;
        return;
    }
    //初始化风险值
    for (int i = 1; i <= traffic_num; ++i)
    {
        risk[begin][end][i] = maxrisk;
    }
    float risks = 0.0;

    //开始递归调用
    route(start, begin, begin, end, risks);
    for (int i = 1; i <= traffic_num; ++i)
    {
        if (risk[begin][end][i] < route_ans)
        {
            route_ans = risk[begin][end][i];
        }
    }
    //有答案
    if ((route_ans != maxrisk) && (route_ans != -1))
    {
        // cout << route_ans << endl;
    }
    //无答案
    else
    {
        route_ans = -1;
        // cout << -1 << endl;
    }
}


void graph::route(int start, int now, int begin, int end, float risks)
{
    // p指针来遍历now节点此时所有的边
    SCHEDULE *p = sche[now].nxt;

    while (p != nullptr)
    {
        // p的路线
        ROUTE x = p->route;
        //这条路的到达时间改为当前时间
        x.clock = start;
        int timing;
        //调整时间,坐上每一辆车
        //如果晚于这个车次的出发时间了,第二天坐
        if (x.start_time < x.clock)
        {
            timing = 24 - x.clock;
            timing += x.start_time;
            x.clock = x.end_time;
        }
        //等今天发车时坐车
        else
        {
            timing = x.start_time - x.clock;
            x.clock = x.end_time;
        }
        //风险增量
        float inc = timing * city[x.start_city].risk;
        //到达目的节点
        if (x.end_city == end)
        {
            risk[begin][end][x.no] = min(risk[begin][end][x.no], inc + risks);
            x.use = true;
        }
        //当前这条路线没用过
        else if (x.use == false)
        {
            //现在用过了
            p->route.use = true;
            float temp = risks + inc;

            //更新经过这条边后,起点到终点的最小风险,仅便于剪枝
            risk[x.start_city][x.end_city][x.no] = min(risk[x.start_city][x.end_city][x.no], temp);
            //继续向下走
            route(x.clock, x.end_city, begin, end, temp);
        }
        //这条路线用过
        else
        {
            //如果这条线之前用过,且这次走的路不优于之前,则说明这条路不够好,退出
            if (inc + risks >= risk[x.start_city][x.end_city][x.no])
            {
            }
            //这条路之前用过,但是这次走的路程比之前所用风险更低,则更新
            else
            {
                risk[x.start_city][x.end_city][x.no] = inc + risks;
                float temp = risks + inc;

                //继续向下走
                route(x.clock, x.end_city, begin, end, temp);
            }
        }
        //找下一条边
        p = p->nxt;
    }
}

对于限时算法
就很简单了,只需要用一个变量来记录时间,如果现在超时了就直接return掉就好,更简单了,算法复杂度更低了

接下来就是说一下细枝末节的问题了
如何接收用户的输入,来返回当前的时间?
用kbhit函数(当然这个地方我调了2个小时,还是比较麻烦的,但是不难)
如何获取获得最优解的线路?
用最笨的方法,用一个临时变量先存起来最优解,然后把数组初始化,重新跑一遍最短路算法,如果当前路的风险值与最优解相等,直接在return的过程中,将路线放入栈中就成功收集到了

源代码

各种数据结构的头文件

#include <iostream>
#include <Windows.h>
#include <vector>
#include <cstdlib>
#include <time.h>
#include <stack>
#include <queue>
#include <fstream>
#include <conio.h>
using namespace std;
//高中低风险度
#define high 0.9
#define mid 0.5
#define low 0.2
//出行方式
#define flg 1
#define tra 2
#define car 3
//乘客状态
#define wait 1
#define way 2

//时间、距离、风险值的无穷大值
#define maxtime 1e4;
#define maxleng 1e4;
const float maxrisk = 1e4;

//直达交通路线
struct ROUTE
{
    //车次号、起止城市、起止时间、跨天数、交通工具
    //辅助记录用的是否使用过,到start城市的时间,此时携带的risk
    int no;
    int start_city, end_city;
    int start_time, end_time;
    int day;
    int tool;
    bool use;
    int clock;
    float risk;
    ROUTE()
    {
        tool = 3;
        clock = 0;
        use = false;
        no = -1;
        risk = 0;
    }
};

//不同城市的时刻表
struct SCHEDULE
{
    ROUTE route;
    SCHEDULE *nxt = nullptr;
    SCHEDULE()
    {
        nxt = nullptr;
    }
};

struct CITY
{
    //城市名
    string name;
    //风险值
    float risk;
    //该节点为起始点的路线数
    int num;
};

class graph
{
private:
    //城市数
    int city_num;
    //交通工具数
    int traffic_num;
    //风险值
    float ***risk;
    //城市
    CITY *city;
    //不同城市为起点的时刻表
    SCHEDULE *sche;

    //记录最短路线
    stack<ROUTE> s;

    //用于程序查询
    stack<ROUTE> find;

    //记录不限时和限时的答案风险值
    float route_ans;
    float time_route_ans;

public:
    graph(int c_n, int r_n)
    {
        city_num = c_n;
        route_ans = maxrisk;
        time_route_ans = maxrisk;
        traffic_num = r_n;
    }

    //初始化
    void init();
    //重置
    void reset();
    //为了输出路径而进行的重置
    void resetroute(int begin, int end);

    //添加节点
    void addnode(int _no, string &_name, float risk);
    //添加边
    void addroute(ROUTE &r);

    //不限时最短路
    void routing(int start, int begin, int end);
    //不限时最短路调用递归函数
    void route(int start, int now, int begin, int end, float risks);

    //查询不限时最短路
    void getroute(int start, int now, int begin, int end, float risks, bool &flag);
    //查询限时最短路
    void gettimeroute(int day, int start, int now, int begin, int end, float risks, int large, bool &flag);

    //限时最短路
    void timerouting(int start, int begin, int end, int large);
    //限时最短路调用递归函数
    void timeroute(int day, int start, int now, int begin, int end, float risks, int large);

    //输出不限时最短路路径
    void output(int start, int begin, int end);
    //输出限时最短路路径
    void timeoutput(int start, int begin, int end, int large);

    //返回路径栈的
    stack<ROUTE> ask()
    {
        stack<ROUTE> res;
        while(!find.empty())
        {
            res.push(find.top());
            find.pop();
        }
        return res;
    }

    //得到城市数
    int getcitynum()
    {
        return city_num;
    }
    //返回城市数组
    CITY *getcity()
    {
        return city;
    }
    //返回不限时/限时的风险值
    float get_route_ans() { return route_ans; }
    float get_routime_ans() { return time_route_ans; }
};

初始化函数及重置函数所在文件

#include "RA.h"
void graph::init()
{
    while (!s.empty())
    {
        s.pop();
    }
    city = new CITY[city_num + 1];
    risk = new float **[city_num + 1];
    sche = new SCHEDULE[city_num + 1];

    for (int i = 1; i <= city_num; ++i)
    {
        risk[i] = new float *[city_num + 1];
        for (int j = 1; j <= city_num; ++j)
        {
            risk[i][j] = new float[traffic_num + 1];
        }
        city[i].num = 1;
    }
    for (int i = 1; i <= city_num; ++i)
    {
        for (int j = 1; j <= city_num; ++j)
        {
            for (int m = 1; m <= traffic_num; ++m)
            {
                if (i != j)
                {
                    risk[i][j][m] = maxrisk;
                }
                else
                {
                    risk[i][j][m] = 0;
                }
            }
        }
    }
}

void graph::addnode(int _no, string &_name, float risk)
{
    city[_no].name = _name;
    city[_no].risk = risk;
}

void graph::addroute(ROUTE &r)
{
    ROUTE temp = r;
    int start = r.start_city;
    int end = r.end_city;
    SCHEDULE *new_one;
    new_one = new SCHEDULE();
    new_one->route = temp;
    new_one->nxt = nullptr;
    if (city[start].num == 1)
    {
        ++city[start].num;
        sche[start].nxt = new_one;
    }
    else
    {
        if (city[start].num == 1)
        {
            sche[start].nxt->nxt = new_one;
            ++city[start].num;
            return;
        }
        SCHEDULE *p = sche[start].nxt;
        SCHEDULE *before = nullptr;
        while (p != nullptr)
        {
            before = p;
            p = p->nxt;
        }
        ++city[start].num;
        before->nxt = new_one;
    }
}

边角功能函数

#include "RA.h"

void graph::reset()
{
    //初始化开空间
    for (int i = 1; i <= city_num; ++i)
    {
        SCHEDULE *p = sche[i].nxt;
        while (p != nullptr)
        {
            SCHEDULE *tmp = p->nxt;
            delete p;
            p = tmp;
        }
    }

    for (int i = 1; i <= city_num; ++i)
    {
        for (int j = 1; j <= city_num; ++j)
        {
            for (int m = 1; m <= traffic_num; ++m)
            {
                if (i != j)
                {
                    risk[i][j][m] = maxrisk;
                }
                else
                {
                    risk[i][j][m] = 0;
                }
            }
        }
    }
}

void graph::resetroute(int begin, int end)
{
    for (int i = 1; i <= city_num; ++i)
    {
        SCHEDULE *p = sche[i].nxt;
        while (p != nullptr)
        {
            p->route.use = false;
            p = p->nxt;
        }
    }
}

void graph::timeoutput(int start, int begin, int end, int large)
{
    int day = 0;
    ofstream fout;
    fout.open("i.out", ios::out);
    if (!fout.is_open())
    {
        return;
    }
    //无解
    if (time_route_ans == -1)
    {
        fout << "No Path" << endl;
        return;
    }
    resetroute(begin, end);
    bool flag = false;
    large += start;
    gettimeroute(day, start, begin, begin, end, 0.0, large, flag);
    int d = 1;
    int now_time = start;
    while (!s.empty())
    {
        //暂存入栈中
        ROUTE tmp = s.top();
        find.push(tmp);
        //是当天可以出行
        if (tmp.start_time >= now_time)
        {
        }
        else
        {
            ++d;
        }
        fout << "第" << d << "天:";
        fout << "乘坐";
        if (tmp.tool == 1)
        {
            fout << tmp.no << "号"
                 << "飞机" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        else if (tmp.tool == 2)
        {
            fout << tmp.no << "号"
                 << "火车" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        else if (tmp.tool == 3)
        {
            fout << tmp.no << "号"
                 << "汽车" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        s.pop();
        now_time = tmp.end_time;
    }
    fout << "到达目的地" << endl;
    fout.close();
}

void graph::output(int start, int begin, int end)
{
    ofstream fout;
    fout.open("i.out", ios::out);
    if (!fout.is_open())
    {
        return;
    }
    //如果无解
    if (route_ans == -1)
    {
        fout << "No Path" << endl;
        return;
    }
    resetroute(begin, end);
    for (int i = 1; i <= city_num; ++i)
    {
        for (int j = 1; j <= city_num; ++j)
        {
            for (int m = 1; m <= traffic_num; ++m)
            {
                if (i != j)
                {
                    risk[i][j][m] = maxrisk;
                }
                else
                {
                    risk[i][j][m] = 0;
                }
            }
        }
    }
    bool flag = false;
    getroute(start, begin, begin, end, 0.0, flag);
    int d = 1;
    int now_time = start;
    while (!s.empty())
    {
        ROUTE tmp = s.top();
        find.push(tmp);
        //是当天可以出行
        if (tmp.start_time >= now_time)
        {
        }
        else
        {
            ++d;
        }
        fout << "第" << d << "天:";
        fout << "乘坐";
        if (tmp.tool == 1)
        {
            fout << tmp.no << "号"
                 << "飞机" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        else if (tmp.tool == 2)
        {
            fout << tmp.no << "号"
                 << "火车" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        else if (tmp.tool == 3)
        {
            fout << tmp.no << "号"
                 << "汽车" << endl;
            fout << tmp.start_time << "点从" << city[tmp.start_city].name << "出发" << endl;
            fout << tmp.end_time << "点到达" << city[tmp.end_city].name << endl;
            fout << endl;
        }
        now_time = tmp.end_time;
        s.pop();
    }
    fout << "到达目的地";
    fout.close();
}

不限时最低风险函数

#include "RA.h"

void graph::routing(int start, int begin, int end)
{
    if (begin == end)
    {
        route_ans = -1;
        // cout << 0;
        return;
    }
    //初始化风险值
    for (int i = 1; i <= traffic_num; ++i)
    {
        risk[begin][end][i] = maxrisk;
    }
    float risks = 0.0;

    //开始递归调用
    route(start, begin, begin, end, risks);
    for (int i = 1; i <= traffic_num; ++i)
    {
        if (risk[begin][end][i] < route_ans)
        {
            route_ans = risk[begin][end][i];
        }
    }
    //有答案
    if ((route_ans != maxrisk) && (route_ans != -1))
    {
        // cout << route_ans << endl;
    }
    //无答案
    else
    {
        route_ans = -1;
        // cout << -1 << endl;
    }
}

void graph::route(int start, int now, int begin, int end, float risks)
{
    // p指针来遍历now节点此时所有的边
    SCHEDULE *p = sche[now].nxt;

    while (p != nullptr)
    {
        // p的路线
        ROUTE x = p->route;
        //这条路的到达时间改为当前时间
        x.clock = start;
        int timing;
        //调整时间,坐上每一辆车
        //如果晚于这个车次的出发时间了,第二天坐
        if (x.start_time < x.clock)
        {
            timing = 24 - x.clock;
            timing += x.start_time;
            x.clock = x.end_time;
        }
        //等今天发车时坐车
        else
        {
            timing = x.start_time - x.clock;
            x.clock = x.end_time;
        }
        //风险增量
        float inc = timing * city[x.start_city].risk;
        //到达目的节点
        if (x.end_city == end)
        {
            risk[begin][end][x.no] = min(risk[begin][end][x.no], inc + risks);
            x.use = true;
        }
        //当前这条路线没用过
        else if (x.use == false)
        {
            //现在用过了
            p->route.use = true;
            float temp = risks + inc;

            //更新经过这条边后,起点到终点的最小风险,仅便于剪枝
            risk[x.start_city][x.end_city][x.no] = min(risk[x.start_city][x.end_city][x.no], temp);
            //继续向下走
            route(x.clock, x.end_city, begin, end, temp);
        }
        //这条路线用过
        else
        {
            //如果这条线之前用过,且这次走的路不优于之前,则说明这条路不够好,退出
            if (inc + risks >= risk[x.start_city][x.end_city][x.no])
            {
            }
            //这条路之前用过,但是这次走的路程比之前所用风险更低,则更新
            else
            {
                risk[x.start_city][x.end_city][x.no] = inc + risks;
                float temp = risks + inc;

                //继续向下走
                route(x.clock, x.end_city, begin, end, temp);
            }
        }
        //找下一条边
        p = p->nxt;
    }
}

void graph::getroute(int start, int now, int begin, int end, float risks, bool &flag)
{
    // // the_road代表当前所用的tour指针
    // TOUR *the_road = ro;

    // p指针来遍历now节点此时所有的边
    SCHEDULE *p = sche[now].nxt;

    while (p != nullptr)
    {

        // p的路线
        ROUTE x = p->route;
        //这条路的到达时间改为当前时间
        x.clock = start;
        int timing;
        //调整时间,坐上每一辆车
        //如果晚于这个车次的出发时间了,第二天坐
        if (x.start_time < x.clock)
        {
            timing = 24 - x.clock;
            timing += x.start_time;
            x.clock = x.end_time;
        }
        //等今天发车时坐车
        else
        {
            timing = x.start_time - x.clock;
            x.clock = x.end_time;
        }
        //风险增量
        float inc = timing * city[x.start_city].risk;
        //到达目的节点
        if (x.end_city == end)
        {
            if (inc + risks == route_ans)
            {
                s.push(x);
                flag = true;
                return;
            }
            x.use = true;
        }
        //当前这条路线没用过
        else if (x.use == false)
        {
            //现在用过了
            p->route.use = true;
            float temp = risks + inc;

            //更新经过这条边后,起点到终点的最小风险,仅便于剪枝
            risk[x.start_city][x.end_city][x.no] = min(risk[x.start_city][x.end_city][x.no], temp);
            //继续向下走
            getroute(x.clock, x.end_city, begin, end, temp, flag);
        }
        //这条路线用过
        else
        {
            //如果这条线之前用过,且这次走的路不优于之前,则说明这条路不够好,退出
            if (inc + risks >= risk[x.start_city][x.end_city][x.no])
            {
            }
            //这条路之前用过,但是这次走的路程比之前所用风险更低,则更新
            else
            {
                risk[x.start_city][x.end_city][x.no] = inc + risks;
                float temp = risks + inc;

                //继续向下走
                getroute(x.clock, x.end_city, begin, end, temp, flag);
            }
        }
        if (flag)
        {
            s.push(x);
            return;
        }
        //找下一条边
        p = p->nxt;
    }
}

限时最低风险函数

#include "RA.h"

void graph::timerouting(int start, int begin, int end, int large)
{
    if (begin == end)
    {
        time_route_ans = -1;
        // cout << 0;
        return;
    }
    //让时间调整到方便使用的时间
    //比如40小时内,但是是7点开始走,则时间调整为47
    large += start;
    //初始化风险值
    for (int i = 1; i <= traffic_num; ++i)
    {
        risk[begin][end][i] = maxrisk;
    }
    float risks = 0.0;

    //开始递归调用
    timeroute(0, start, begin, begin, end, risks, large);
    for (int i = 1; i <= traffic_num; ++i)
    {
        if (risk[begin][end][i] < time_route_ans)
        {
            time_route_ans = risk[begin][end][i];
        }
    }
    if (time_route_ans != -1 && time_route_ans != maxrisk)
    {
        // cout << time_route_ans << endl;
    }
    else
    {
        time_route_ans = -1;
        // cout << -1 << endl;
    }
}

void graph::timeroute(int day, int start, int now, int begin, int end, float risks, int large)
{
    //先记录下当前的时间,防止day重复改变
    int real_day = day;
    //超时
    if (day * 24 + start > large)
    {
        return;
    }
    // p指针来遍历now节点此时所有的边
    SCHEDULE *p = sche[now].nxt;

    while (p != nullptr)
    {
        //让day取得最初的时间
        day = real_day;
        // p的路线
        ROUTE x = p->route;
        //这条路的到达时间改为当前时间
        x.clock = start;
        int timing;
        //调整时间,坐上每一辆车
        //如果晚于这个车次的出发时间了,第二天坐
        if (x.start_time < x.clock)
        {
            timing = 24 - x.clock;
            timing += x.start_time;
            x.clock = x.end_time;
            // day+1,意为隔一天了
            ++day;
        }
        //等今天发车时坐车
        else
        {
            timing = x.start_time - x.clock;
            x.clock = x.end_time;
        }
        //风险增量
        float inc = timing * city[x.start_city].risk;
        //到达目的节点
        if (x.end_city == end)
        {
            //如果时间未超时
            if ((x.clock + day * 24 <= large))
            {
                risk[begin][end][x.no] = min(risk[begin][end][x.no], inc + risks);
            }
            x.use = true;
        }
        //当前这条路线没用过
        else if (x.use == false)
        {
            //现在用过了
            p->route.use = true;
            float temp = risks + inc;

            //更新经过这条边后,起点到终点的最小风险,仅便于剪枝
            risk[x.start_city][x.end_city][x.no] = min(risk[x.start_city][x.end_city][x.no], temp);
            //继续向下走
            timeroute(day, x.clock, x.end_city, begin, end, temp, large);
        }
        //这条路线用过
        else
        {
            //如果这条线之前用过,且这次走的路不优于之前,则说明这条路不够好,退出
            if (inc + risks >= risk[x.start_city][x.end_city][x.no])
            {
            }
            //这条路之前用过,但是这次走的路程比之前所用风险更低,则更新
            else
            {
                risk[x.start_city][x.end_city][x.no] = inc + risks;
                float temp = risks + inc;

                //继续向下走
                timeroute(day, x.clock, x.end_city, begin, end, temp, large);
            }
        }
        //找下一条边
        p = p->nxt;
    }
}

void graph::gettimeroute(int day, int start, int now, int begin, int end, float risks, int large, bool &flag)
{
    //先记录下当前的时间,防止day重复改变
    int real_day = day;
    //超时
    if (day * 24 + start > large)
    {
        return;
    }
    // p指针来遍历now节点此时所有的边
    SCHEDULE *p = sche[now].nxt;

    while (p != nullptr)
    {
        //让day取得最初的时间
        day = real_day;
        // p的路线
        ROUTE x = p->route;
        //这条路的到达时间改为当前时间
        x.clock = start;
        int timing;
        //调整时间,坐上每一辆车
        //如果晚于这个车次的出发时间了,第二天坐
        if (x.start_time < x.clock)
        {
            timing = 24 - x.clock;
            timing += x.start_time;
            x.clock = x.end_time;
            // day+1,意为隔一天了
            ++day;
        }
        //等今天发车时坐车
        else
        {
            timing = x.start_time - x.clock;
            x.clock = x.end_time;
        }
        //风险增量
        float inc = timing * city[x.start_city].risk;
        //到达目的节点
        if (x.end_city == end)
        {
            //如果时间未超时,且就是答案
            if ((x.clock + day * 24 <= large) && (inc + risks == time_route_ans))
            {
                flag = true;
                s.push(x);
                return;
            }
            x.use = true;
        }
        //当前这条路线没用过
        else if (x.use == false)
        {
            //现在用过了
            p->route.use = true;
            float temp = risks + inc;

            //更新经过这条边后,起点到终点的最小风险,仅便于剪枝
            risk[x.start_city][x.end_city][x.no] = min(risk[x.start_city][x.end_city][x.no], temp);
            //继续向下走
            gettimeroute(day, x.clock, x.end_city, begin, end, temp, large, flag);
        }
        //这条路线用过
        else
        {
            //如果这条线之前用过,且这次走的路不优于之前,则说明这条路不够好,退出
            if (inc + risks >= risk[x.start_city][x.end_city][x.no])
            {
            }
            //这条路之前用过,但是这次走的路程比之前所用风险更低,则更新
            else
            {
                risk[x.start_city][x.end_city][x.no] = inc + risks;
                float temp = risks + inc;

                //继续向下走
                gettimeroute(day, x.clock, x.end_city, begin, end, temp, large, flag);
            }
        }
        if (flag)
        {
            s.push(x);
            return;
        }
        //找下一条边
        p = p->nxt;
    }
}

主函数

#include "RA.h"

void read(string &file_name, graph &g);

void asking(int time, graph &g, int start);

void desk();

int main()
{
    desk();
    string file;
    printf("请输入数据地址所在位置:\n");
    printf("例如:C:/Users/5115/Desktop/4744303660525717198/5151/i.in\n");
     cin >> file;

    ifstream fin;
    fin.open(file, ios::in);
    if (!fin.is_open())
    {
        cout << "No file\n";
        return 0;
    }

    int city_num;
    int route_num;
    fin >> city_num >> route_num;
    graph g(city_num, route_num);
    g.init();
    read(file, g);
    printf("请选择是否限时出行\n1->限时\n2->不限时\n");
    int xxxx;
    cin >> xxxx;
    int stm;
    if (xxxx == 1)
    {
        int stc, edc, strtime;
        printf("请分别输入出行起始时间,出行起始城市,目的城市,限时小时数\n");
        cin >> stm >> stc >> edc >> strtime;
        g.timerouting(stm, stc, edc, strtime);
        g.timeoutput(stm, stc, edc, strtime);
        printf("已输出到对应输出文件\n");
        if (g.get_routime_ans() == -1)
        {
            printf("没有找到符合条件路线\n");
            return 0;
        }
    }
    else
    {
        int stc, edc;
        printf("请分别输入出行起始时间,出行起始城市,目的城市\n");
        cin >> stm >> stc >> edc;
        g.routing(stm, stc, edc);
        g.output(stm, stc, edc);
        printf("已输出到对应输出文件\n");
        if (g.get_route_ans() == -1)
        {
            printf("没有找到符合条件路线\n");
            return 0;
        }
    }
    printf("请输入模拟时间秒数,如输入1代表1s模拟路线运行1h\n");
    int time;
    cin >> time;
	printf("\n可以随时输入1进行查询此时旅客状态\n");
    asking(time, g, stm);
    return 0;
}

void desk()
{
    printf("原作者:sdu_cs_s某\n");
    printf("提示:该程序只能按照数据要求进行输入数据,若不按数据要求输入,则程序大概率会出错\n");
    printf("该程序不支持路线运行跨时超过24h的车次\n");
    printf("程序介绍:\n");
	printf("本程序为风险出行系统,分为限时和不限时两种模式,同时可以用实际时间模拟程序时间\n");
    printf("例如可以现实生活每1s,模拟路线运行1h,实际时间最小单位为1s,模拟时间最小单位为1h\n");
    printf("如果有出行路线,则会在该文件夹下建立一个i.out文件,包含路线\n");
    printf("本程序支持自定义输入规模数据,由于数据量较大,只支持文件规模读入,数据文件格式如下\n");
    printf("城市数 路线数\n");
    printf("以下城市数行需要输入(输入顺序即为城市顺序,例如第一行输入的城市,其序号为1):\n城市名 城市风险值(0.2/0.5/0.9)\n");
    printf("以下路线数行需要输入(路线方式输入1代表飞机,2代表火车,3代表汽车):起始城市序号 结束城市序号 该路线起始时间 该路线结束时间 路线方式\n");
}

void read(string &file_name, graph &g)
{
    ifstream fin;
    fin.open(file_name, ios::in);
    if (!fin.is_open())
    {
        cout << "No File";
        return;
    }
    int city;
    int rou;
    fin >> city >> rou;
    for (int i = 1; i <= city; ++i)
    {
        string name;
        float ri;
        fin >> name >> ri;
        g.addnode(i, name, ri);
    }
    for (int i = 1; i <= rou; ++i)
    {
        ROUTE r;
        int sc, ec, st, et, to;
        fin >> sc >> ec >> st >> et >> to;
        r.start_city = sc;
        r.end_city = ec;
        r.start_time = st;
        r.end_time = et;
        r.tool = to;
        r.no = i;
        g.addroute(r);
    }
    fin.close();
}

void asking(int time, graph &g, int start)
{
    CITY *city = new CITY[g.getcitynum() + 1];
    CITY *tmp = g.getcity();
    for (int i = 1; i <= g.getcitynum(); ++i)
    {
        city[i].name = tmp[i].name;
        city[i].risk = tmp[i].risk;
    }
    //获取路线顺序
    stack<ROUTE> x = g.ask();

    //得到总的运行小时数
    queue<ROUTE> q;
    stack<ROUTE> bak;

    int all_time = 0;
    if (!x.empty())
    {
        all_time = x.top().start_time - start;
    }
    else
    {
        cout << "No path" << endl;
        return;
    }
    while (!x.empty())
    {
        q.push(x.top());
        bak.push(x.top());
        x.pop();
    }
    while (!q.empty())
    {
        int before_end = q.front().end_time;
        int before_start = q.front().start_time;
        x.push(bak.top());
        bak.pop();
        q.pop();
        all_time += (before_end - before_start);
        if (q.empty())
        {
            break;
        }
        //加上两段旅程中间的过渡时间
        else
        {
            int after_end = q.front().end_time;
            int after_start = q.front().start_time;
            if (after_start >= before_end)
            {
                all_time += (after_start - before_end);
            }
            else
            {
                all_time += (24 - before_end + after_start);
            }
        }
    }

    int clk = start;
    int length = 0;
    int before = 0;
    clock_t s, m;
    s = clock();
    while (!x.empty())
    {
        //如果有键盘输入
        if (_kbhit())
        {
            char trash = _getch();
            m = clock();
            before = length;
            length = (m - s) / (1000 * time);
            //tp是两次查询的时间差
            int tp = length - before;
            // tp是需要遍历的小时数
            while (tp)
            {
                --tp;
                ++clk;
                //跨到第二天了
                if (clk >= 24)
                {
                    clk -= 24;
                }

                //当前这辆车次已经遍历完了
                if (clk > x.top().end_time)
                {
                    int et = x.top().end_time;
                    //下一辆
                    x.pop();
                    if (x.empty())
                    {
                        break;
                    }

                    int nxt = x.top().start_time;
                    //说明下一辆车跨天了
                    if (nxt < et)
                    {
                        //需要调整时钟到第二天
                        // inc是时钟需要推进的小时数
                        int inc = 24 - clk;
                        //如果剩余时间不足以推进到第二天,则用完
                        if (tp < inc)
                        {
                            clk += tp;
                            break;
                        }
                        //剩余时间足够推进到第二天,则进行推进到当天最后一小时,下次直接跨天
                        else
                        {
                            tp -= inc;
                            //因为不推到第二天,所以要补一个小时回来
                            ++tp;
                            clk = 23;
                        }
                    }
                }
            }
            if (x.empty())
            {
                break;
            }
            //在此时的车次上
            if (clk >= x.top().start_time && clk <= x.top().end_time)
            {
                ROUTE r = x.top();
                cout << clk << "点";
                cout << "在" << r.no << "号";
                cout << "从" << city[r.start_city].name << "前往";
                cout << city[r.end_city].name << "的";
                if (r.tool == 1)
                {
                    cout << "飞机上" << endl;
                }
                else if (r.tool == 2)
                {
                    cout << "火车上" << endl;
                }
                else
                {
                    cout << "汽车上" << endl;
                }
            }
            //还坐不到下一辆车
            else
            {
                if (x.empty())
                {
                    break;
                }
                ROUTE r = x.top();
                int now_place = r.start_city;
                int r_st = r.start_time;
                int r_et = r.end_time;
                cout << clk << "点在";
                cout << city[now_place].name;
                cout << "等待" << r.no << "号";
                if (r.tool == 1)
                {
                    cout << "飞机" << endl;
                }
                else if (r.tool == 2)
                {
                    cout << "火车" << endl;
                }
                else
                {
                    cout << "汽车" << endl;
                }
            }
        }
        m = clock();
        //说明程序运行结束了,依旧没有查询,直接结束
        if ((m - s) / (1000 * time) >= all_time)
        {
            break;
        }
    }
    cout << "已到达终点" << endl;
}

i.in(数据文件)

12 35
HaErBin 0.9
ChangSha 0.9
ChengDu 0.9
HeFei 0.9
BeiJing 0.5
ShangHai 0.5
TianJin 0.5
ChongQing 0.5
XiaMen 0.2
LanZhou 0.2
GuiLin 0.2
ChangChun 0.2
9 4 6 8 1
4 9 10 12 1
1 6 12 15 1
6 1 18 21 1
9 3 12 16 1
8 4 7 9 1
10 12 4 8 1

3 5 10 13 2
5 3 14 17 2
12 5 16 18 2
5 12 20 22 2 
7 12 2 12 2
12 7 13 23 2
4 6 11 14 2
6 4 15 18 2 
3 6 6 19 2
9 8 7 19 2
8 7 8 20 2
12 5 8 14 2
5 12 16 22 2
10 4 4 13 2
10 4 19 23 2
4 10 14 18 2
4 3 0 11 2
3 4 12 23 2

2 1 10 12 3
2 11 3 6 3
2 11 6 8 3
11 6 3 9 3
11 1 3 7 3
2 8 3 10 3

数据图
在这里插入图片描述

写在最后

本实验是s某原创,如果有更好的想法欢迎和我交流,有不懂的地方也可以直接私信我
希望学弟学妹们能想到更好的想法(如可视化界面设计,或者更优的算法)

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mac-lengs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值