「GXOI & GZOI 2019」 旅行者

Description

给你 \(n\) 个点 \(m\) 条边的有向图,求给定 \(k\) 个特殊点两两之间最短路的最小值。\(T\) 组询问。

\(1 \leq T \leq 5\)

\(1 \leq k \leq n \leq 10^5\)

\(1 \leq m \leq 5 \times 10^5\)

\(1 \leq z \leq 2 \times 10^9\)

Solution

两种思路。

第一种:

对于每一条边 \((x,y,z)\) ,离 \(x\) 最近的特殊点是 \(a\),离 \(y\) 最近的特殊点是 \(b\)。那么,这两个特殊点的距离是 \(dis_a + dis_b + z\)。我们要找到最小值。

因为这是有向图,所以正反跑两次 Dijkstra 就可以了进行染色就可以了,染色是记录离每个城市最近的感兴趣的城市。如果 \(a = b\),那么就相当于走了一个环,所以我们要特判。

复杂度 \(O(T \times nlogn)\)

第二种:

将所有的点分成两个集合 \(A\)\(B\)。我们用源点 \(s\) 连接集合 \(A\) 中所有的点,边权都为 \(0\),再将集合 \(B\) 连向汇点 \(t\),边权都为 \(0\)。集合内部边权不变。

将所有节点分成两个集合,要满足任何两个特殊点都有至少一次被分进了不同的集合。可以按照每个点的编号的二进制的第 \(i\) 位是 \(0\) 还是 \(1\) 枚举。只要是两个不同的点,一定会被分进两个不同的集合。同样,因为也是有向图,也要跑两边 Dijkstra。

复杂度 \(O(T \times nlog^2n)\)

\(long \ long\),注意无穷大的值,注意 \(<\) 的重载。两遍 Dijkstra 时间复杂度低,代码实现容易,所以只写两边 Dijkstra做法。二进制枚举也可行,但只拓宽思路而不推荐。

Code

#include <bits/stdc++.h>
using namespace std;
#define re register
#define F first
#define S second
typedef long long ll;
typedef pair<int, int> P;
const int N = 1e6 + 6;
const int INF = 0x3f3f3f3f;
inline int read() {
    int X = 0,w = 0; char ch = 0;
    while(!isdigit(ch)) {w |= ch == '-';ch = getchar();}
    while(isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48),ch = getchar();
    return w ? -X : X;
}
int Start[N], To[N], Dis[N], color[2][N], a[N], head[N], tot;
ll dis[2][N];
int n, m, k;
struct edge{
    int to, w, nxt;
}e[N];
inline void addedge(int x, int y, int z){
    e[++tot].to = y; e[tot].w = z; e[tot].nxt = head[x]; head[x] = tot;
}
struct node{
    int x; ll w;
};
bool operator < (node x, node y){
    return x.w > y.w;
}
void Dijkstra(int p){
    memset(color[p], 0, sizeof(color[p])); memset(dis[p], 0x7f, sizeof(dis[p]));
    priority_queue <node> q;
    for (re int i = 1; i <= k; i++) {
        dis[p][a[i]] = 0; color[p][a[i]] = a[i];
        q.push((node){a[i], 0});
    }
    while (!q.empty()){
        int x = q.top().x; ll w = q.top().w; q.pop();
        if (w != dis[p][x]) continue;
        for (re int i = head[x]; i; i = e[i].nxt){
            int y = e[i].to, ww = e[i].w;
            if (dis[p][y] > dis[p][x] + ww){
                dis[p][y] = dis[p][x] + ww;
                color[p][y] = color[p][x];
                q.push((node){y, dis[p][y]});
            }
        }
    }
}
int main(){
    int T = read();
    while (T--){
        n = read(), m = read(), k = read();
        memset(head, 0, sizeof(head)); tot = 0;
        for (re int i = 1; i <= m; i++){
            int x = read(), y = read(), z = read();
            Start[i] = x; To[i] = y; Dis[i] = z;
            if (x != y) addedge(x, y, z);
        }
        for (re int i = 1; i <= k; i++) a[i] = read();
        Dijkstra(0);
        memset(head, 0, sizeof(head)); tot = 0;
        for (int i = 1; i <= m; i++)
            if (Start[i] != To[i]) addedge(To[i], Start[i], Dis[i]);
        Dijkstra(1);
        ll ans = 1e18;
        for (re int i = 1; i <= m; i++){
            int x = Start[i], y = To[i], z = Dis[i];
            if (color[0][x] && color[1][y] && color[0][x] != color[1][y])
                ans = min(ans, z + dis[0][x] + dis[1][y]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/lyfoi/p/11443351.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TSP(Traveling Salesperson Problem)即旅行商问题,是计算机科学中的一个经典问题。该问题的基本形式为,在一个地图上有多个城市,每个城市之间都有一定的距离,要求找到一条最短的路径,使得每个城市都能被访问到且只能访问一次,最后回到起点城市。 对于TSP问题中的多个旅行者,可以理解为有多个旅行销售员,他们要在所有城市之间进行巡回,每个人都要找到一条最短路径,并且每个城市只能被一个人访问一次。这个问题的要求是找到一种使得每个旅行者的路径和最短的方案。 解决TSP问题有多种算法,例如暴力穷举法,分支界限法,动态规划等。而对于TSP问题中的多个旅行者,可以采用一些改进后的算法,如并行遗传算法等。 并行遗传算法可以将问题划分为若干个子问题,每个子问题对应一个旅行销售员,通过并行计算的方式寻找每个旅行销售员的最短路径。在计算过程中,通过交换、变异等方式对每个旅行者的路径进行优化,使得每个人的路径都趋向于最优解。最后,将每个旅行者的路径进行合并,就得到了多个旅行者的TSP问题的解。 总而言之,TSP问题中的多个旅行者要在给定的地图上找到各自最短的路径,并且每个城市只能被一个人访问一次。采用并行遗传算法等优化算法可以有效地解决这个问题,找到最优解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值