洛谷 P5764 [CQOI2005]新年好

P5764 [CQOI2005]新年好

题目描述

重庆城里有 n n n 个车站, m m m 条双向公路连接其中的某些车站。每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同。在一条路径上花费的时间等于路径上所有公路需要的时间之和。

佳佳的家在车站 1 1 1,他有五个亲戚,分别住在车站 a , b , c , d , e a,b,c,d,e a,b,c,d,e。过年了,他需要从自己的家出发,拜访每个亲戚(顺序任意),给他们送去节日的祝福。怎样走,才需要最少的时间?

输入格式

第一行: n , m n,m n,m,分别为车站数目和公路的数目。

第二行: a , b , c , d , e a,b,c,d,e a,b,c,d,e,分别为五个亲戚所在车站编号。

以下 m m m 行,每行三个整数 x , y , t x,y,t x,y,t,为公路连接的两个车站编号和时间。

输出格式

仅一行,包含一个整数 T T T,为最少的总时间。保证 T ≤ 1 0 9 T\le 10^9 T109

样例 #1

样例输入 #1

6 6
2 3 4 5 6
1 2 8
2 3 3
3 4 4
4 5 5
5 6 2
1 6 7

样例输出 #1

21

提示

对于 40 % 40\% 40% 的数据,有 1 ≤ n ≤ 500 1≤n≤500 1n500 1 ≤ m ≤ 2000 1≤m≤2000 1m2000

对于 100 % 100\% 100% 的数据,有 1 ≤ n ≤ 50000 1≤n≤50000 1n50000 1 ≤ m ≤ 100000 1≤m≤100000 1m100000 1 ≤ a , b , c , d , e ≤ n 1\le a,b,c,d,e≤n 1a,b,c,d,en 1 ≤ x , y ≤ n 1≤x,y≤n 1x,yn 1 ≤ t ≤ 10000 1≤t≤10000 1t10000

思路

起点确定,所到达的点集有限,且大小固定为5,非常小,于是我们可以爆搜访问点集中每个点的顺序,也就是全排列。在爆搜过程中我们需要知道当前点 x x x到要访问的点 y y y的最短距离,最短距离可以用很多算法求解,本题数据量可知所给出的图为稀疏图,范围比较大,首选堆优化的Dijkstra算法,最短距离需要预先处理,这样在爆搜的过程中离线查询即可。本题的存图方式比较常规,但是记录最短路有些讲究,我们需要开一个二维数组 d i s t [ 7 ] [ N ] dist[7][N] dist[7][N] d i s t [ i ] [ j ] dist[i][j] dist[i][j]表示 s t a r t [ i ] start[i] start[i] j j j的最短路,这样记录最短路的话,我们可以枚举会访问六个点到其他点的最短路。

参考代码(C++)

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 50010, M = 200010, INF = 0x3f3f3f3f;

int n, m, res;
int start[7], dist[7][N];
int h[N], e[M], ne[M], w[M], idx;
bool st[N], vis[6];

void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
}

void dijkstra(int sr, int dist[]) {
    memset(st, 0, sizeof st);
    priority_queue<PII, vector<PII>, greater<PII>> que;

    dist[sr] = 0;
    que.push({0, sr});
    while(que.size()) {
        auto tt = que.top(); que.pop();

        if(st[tt.y]) continue;
        st[tt.y] = true;

        for(int i = h[tt.y]; ~i; i = ne[i]) {
            int j = e[i];
            if(dist[j] > tt.x + w[i]) {
                    dist[j] = tt.x + w[i];
                    que.push({dist[j], j});
            }
        }
    }
}

void dfs(int u, int cost, int p) {
    if(u == 6) {
        res = min(res, cost);
    }

    if(cost > res) return ;

    for(int i = 2; i <= 6; i ++) {
        if(!vis[i]) {
            vis[i] = true;
            dfs(u + 1, cost + dist[p][start[i]], i);
            vis[i] = false;
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);

    start[1] = 1;
    for(int i = 2; i <= 6; i ++) scanf("%d", &start[i]);


    memset(h, -1, sizeof h);
    while(m --) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);
    }

    memset(dist, 0x3f, sizeof dist);
    for(int i = 1; i <= 6; i ++) dijkstra(start[i], dist[i]);

    res = INF;
    dfs(1, 0, 1);

    printf("%d\n", res);
    return 0;
}

疑问

有疑问欢迎私信或者评论,看到消息会解答

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值