Airport Express

题目链接

  • 题意:
    第一行是N,S,E,N个点,S为起点,N为重点;第二行是M,然后M行,每行代表a到b有一个值为c的边;之后是K,每行代表a到b有一个值为c的边。
    对于K条边,最多只能用一条,求S到E的最短路。输出:路径+使用的特殊边的起点(即路径中第一个出现的特殊边的点号,没有的话输出“Ticket Not Used”)+最短距离
  • 思路:
    既然每条特殊边只能用一次,那么先考虑一下枚举每个特殊边。必须使用这条边(a->b)的距离的最小值是多少呢?必然是起点到a的最短距离加上边长加上b到终点的最短距离。分析到这里就可以发现其实对于每条边,我们都需要知道它的两个点分别到起点和终点的最短路,即起点和终点各一次dijskstra算法即可。
  • 重点:
    分析出起点和终点各一次dijskstra算法
    考虑不需要任何特殊边的路径
    每个特殊边应该有两次操作(a到起点的最短路和终点的最短路,两个情况不能少)
    路径的输出
const int MAXV = 600;
const int MAXE = 1100;

//堆优化的Dijkstra
struct Edge
{
    int from, to, dist;
};

struct HeapNode
{
    int d, u;
    bool operator < (const HeapNode& rhs) const
    {
        return d > rhs.d;
    }
};
int ans;
vector<int> vt;

struct Dijkstra
{
    int n, m;
    vector<Edge> edges;
    vector<int> G[MAXV];
    bool done[MAXV];    // 是否已永久标号
    int d[MAXV];        // s到各个点的距离
    int p[MAXV];        // 最短路中的上一条弧

    void init(int n)
    {
        this->n = n;
        for(int i = 0; i < n; i++) G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int dist)
    {
        edges.push_back((Edge) { from, to, dist });
        m = edges.size();
        G[from].push_back(m - 1);
    }

    void dijkstra(int s)
    {
        priority_queue<HeapNode> Q;
        for(int i = 0; i < n; i++) d[i] = INF;
        d[s] = 0;
        memset(done, 0, sizeof(done));
        Q.push((HeapNode) { 0, s });
        while(!Q.empty())
        {
            HeapNode x = Q.top();
            Q.pop();
            int u = x.u;
            if(done[u]) continue;
            done[u] = true;
            for(int i = 0; i < G[u].size(); i++)
            {
                Edge& e = edges[G[u][i]];
                if(d[e.to] > d[u] + e.dist)
                {
                    d[e.to] = d[u] + e.dist;
                    p[e.to] = G[u][i];
                    Q.push((HeapNode) { d[e.to], e.to });
                }
            }
        }
    }

    void display1(int u)
    {
        if (d[u] == 0) goto end;
        display1(edges[p[u]].from);
        end:;
        vt.push_back(u + 1);
    }

    void display2(int u)
    {
        vt.push_back(u + 1);
        if (d[u] == 0) return;
        display2(edges[p[u]].from);
    }
} sd, ed;

void display()
{
    int len = vt.size();
    REP(i, len)
    {
        if (i) putchar(' ');
        printf("%d", vt[i]);
    }
    puts("");
}

int main()
{
//    freopen("in.txt", "r", stdin);
    int N, S, E, M, K, a, b, c, t, u, v;
    int kase = 0;
    while (~RIII(N, S, E))
    {
        if(kase != 0)   printf("\n");
        vt.clear();
        S--; E--;
        ans = INF;
        sd.init(N);
        ed.init(N);

        RI(M);
        REP(i, M)
        {
            RIII(a, b, c); a--; b--;
            sd.AddEdge(a, b, c);
            sd.AddEdge(b, a, c);
            ed.AddEdge(a, b, c);
            ed.AddEdge(b, a, c);
        }
        sd.dijkstra(S);
        ed.dijkstra(E);
        RI(K);
        REP(i, K)
        {
            RIII(a, b, c); a--; b--;
            if ((t = sd.d[a] + ed.d[b] + c) < ans)
            {
                u = a;
                v = b;
                ans = t;
            }
            else if ((t = sd.d[b] + ed.d[a] + c) < ans)
            {
                u = b;
                v = a;
                ans = t;
            }
        }
        if (sd.d[E] < ans)
        {
            ans = sd.d[E];
            sd.display1(E);
            display();
            puts("Ticket Not Used");
        }
        else
        {
            sd.display1(u);
            ed.display2(v);
            display();
            WI(u + 1);
        }
        WI(ans);
        kase = 1;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值