[Luogu P2149] [SDOI2009]Elaxia的路线

本文探讨了一个涉及最短路径算法的问题,通过在包含N个节点和M条边的无向图中找到两对点之间的最长公共路径,以解决Elaxia和w**如何在忙碌的大学生活中最大化共处时间的挑战。解决方案包括使用四次SPFA或Dijkstra算法确定四个关键点的最短路径,然后分析最短路径依赖图(DAG)以找出公共边,最终通过动态规划(dp)获得最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

最近,Elaxia和w**的关系特别好,他们很想整天在一起,但是大学的学习太紧张了,他们 必须合理地安排两个人在一起的时间。

Elaxia和w**每天都要奔波于宿舍和实验室之间,他们 希望在节约时间的前提下,一起走的时间尽可能的长。

现在已知的是Elaxia和w**所在的宿舍和实验室的编号以及学校的地图:地图上有 N N N个路 口, M M M条路,经过每条路都需要一定的时间。 具体地说,就是要求无向图中,两对点间最短路的最长公共路径。

输入输出格式

输入格式:

第一行:两个整数 N N N M M M(含义如题目描述)。

第二行:四个整数 x 1 x_1 x1 y 1 y_1 y1 x 2 x_2 x2 y 2 y_2 y2 1 ≤ x 1 ≤ N 1 ≤ x_1 ≤ N 1x1N 1 ≤ y 1 ≤ N 1 ≤ y_1 ≤ N 1y1N 1 ≤ x 2 ≤ N 1 ≤ x_2 ≤ N 1x2N 1 ≤ y 2 ≤ N 1 ≤ y_2 ≤ N 1y2N),分别表示Elaxia的宿舍和实验室及w**的宿舍和实验室的标号(两对点分别 x 1 , y 1 x_1,y_1 x1,y1 x 2 , y 2 x_2,y_2 x2,y2)。

接下来 M M M行:每行三个整数, u u u v v v l l l 1 ≤ u ≤ N 1 ≤ u ≤ N 1uN 1 ≤ v ≤ N 1 ≤ v ≤ N 1vN 1 ≤ l ≤ 10000 1 ≤ l ≤ 10000 1l10000),表 u u u v v v之间有一条路,经过这条路所需要的时间为 l l l

输出格式:

一行,一个整数,表示每天两人在一起的时间(即最长公共路径的长度)

输入输出样例

输入样例#1:
9 10
1 6 7 8
1 2 1
2 5 2
2 3 3
3 4 2
3 9 5
4 5 3
4 6 4
4 7 2
5 8 1
7 9 1
输出样例#1:
3

说明

对于30%的数据, N ≤ 100 N ≤ 100 N100

对于60%的数据, N ≤ 1000 N ≤ 1000 N1000

对于100%的数据, N ≤ 1500 N ≤ 1500 N1500,输入数据保证没有重边和自环。

解题分析

做4遍 S P F A SPFA SPFA D i j k s t r a Dijkstra Dijkstra,求出四个点的单源最短路径, 进而得到最短路 D A G DAG DAG上的公共边, 最后按从 x 1 x_1 x1出发的最短路 D A G DAG DAG的拓扑序 d p dp dp得到答案。

注意在 x 1 → y 1 x_1\to y_1 x1y1的最短路上可能有 y 2 → x 2 y_2\to x_2 y2x2的一截最短路, 判断的时候应该小心。

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <queue>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1505
#define ME 200500
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
int dot, line, cnt, xx1, xx2, yy1, yy2;
bool inq[MX];
std::queue <int> q;
int st1[MX], ed1[MX], st2[MX], ed2[MX], head[MX], nw[MX], deg[MX];
struct Edge {int from, to, len, val, nex;} edge[ME << 2];
IN void add(R int from, R int to, R int len)
{edge[++cnt] = {from, to, len, 0, head[from]}, head[from] = cnt;}
IN void addnw(R int from, R int to, R int len, R int mul)
{edge[++cnt] = {from, to, len, mul, nw[from]}, nw[from] = cnt;}
IN int SPFA(int *dis, R int st)
{
    std::memset(dis, 63, 4 * MX - 4);
    dis[st] = 0; q.push(st); R int now, i;
    W (!q.empty())
    {
        now = q.front(); q.pop();
        for (i = head[now]; i; i = edge[i].nex)
        {
            if(dis[edge[i].to] > dis[now] + edge[i].len)
            {
                dis[edge[i].to] = dis[now] + edge[i].len;
                if (!inq[edge[i].to]) inq[edge[i].to] = true, q.push(edge[i].to);
            }
        }
        inq[now] = false;
    }
}
void init()
{
    int tmp = cnt;
    for (R int i = 1; i <= tmp; ++i)
    {
        if(st1[edge[i].from] + edge[i].len + ed1[edge[i].to] == st1[yy1])//on the fisrt
        {
            if(st2[edge[i].from] + edge[i].len + ed2[edge[i].to] == st2[yy2] || st2[edge[i].to] + edge[i].len + ed2[edge[i].from] == ed2[xx2])
            addnw(edge[i].from, edge[i].to, edge[i].len, 1);
            else addnw(edge[i].from, edge[i].to, edge[i].len, 0);
            ++deg[edge[i].to];
        }
    }
}
IN void SPFA2(int *dis, R int st)
{
    std::memset(dis, -1, MX * 4 - 4); dis[xx1] = 0;
    q.push(xx1); R int now, i;
    W (!q.empty())
    {
        now = q.front(); q.pop();
        for (i = nw[now]; i; i = edge[i].nex)
        {
            if(!(--deg[edge[i].to])) q.push(edge[i].to);
            dis[edge[i].to] = std::max(dis[edge[i].to], dis[now] + edge[i].len * edge[i].val);
        }
    }
}
int main(void)
{
    int a, b, c;
    in(dot), in(line), in(xx1), in(yy1), in(xx2), in(yy2);
    for (R int i = 1; i <= line; ++i)
    {
        in(a), in(b), in(c);
        add(a, b, c), add(b, a, c);
    }
 	SPFA(st1, xx1); SPFA(ed1, yy1); SPFA(st2, xx2); SPFA(ed2, yy2);
    init(); SPFA2(st1, xx1);
    printf("%d", st1[yy1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值