时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

a180285非常喜欢滑雪。他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi。a180285 能从景点i 滑到景点j 当且仅当存在一条i 和j 之间的边,且i 的高度不小于j。
与其他滑雪爱好者不同,a180285喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。于是a180285拿出了他随身携带的时间胶囊。
这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间 之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。
现在,a180285站在1号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间 胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前 提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?

输入描述:

输入的第一行是两个整数N,M。
接下来1行有N个整数Hi,分别表示每个景点的高度。
接下来M行,表示各个景点之间轨道分布的情况。每行3个整数,Ui,Vi,Ki。表示编号为Ui的景点和编号为Vi的景点之间有一条长度为Ki的轨道。

输出描述:

输出一行,表示a180285最多能到达多少个景点,以及此时最短的滑行距离总和。
示例1

输入

复制
3 3 
3 2 1 
1 2 1 
2 3 1 
1 3 10

输出

复制
3 2

解题思路

仅仅抓住可以返回这个地方,又要去尽可能多的节点,花费最小,这和最小生成树很类似,但是如果我们直接吧给的边按图片说明 去求解会发现一个问题,原来最小生成树是无向图的,现在不是全部的节点都可以按照权值前往。因为这是一个有向边,并且点权也会影响边的走向。那么怎么办呢,在建图这个地方下功夫。

输入起点终点权值,按照高度比较,高向低存在路径,建立边。
通过一次广度优先遍历,吧从当前节点可以去的全部地方,标记起来,后序图片说明去求解答案的时候,需要判定,起点和终点所在集合是不是被标记的节点集合,如果其中一个,不是说明无法去到其中一个节点,不能把这个连通块进行合并。
并且在图片说明求解前,按照高度为第一关键字降序排序,边权为第二关键字升序排序,保证处理节点之后返回能到达其余节点,并且花费最小。
时间复杂度的话 图片说明

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=43650470&returnHomeType=1&uid=919749006

Code

#include <bits/stdc++.h>
#pragma GCC optimize(2) //如果选择vector  记得开O2 O3优化,虽然我开了还是随缘超时,一直被卡一个样例
#pragma GCC optimize(3)
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
const int MAX = 1e6 + 7;
int n, m, h[MAX];//点数,边数,点高度
int head[MAX], tot = 0;//前向星变量
struct Node {
    int u, v, w, next;
    bool operator < (const Node& b)const {
        if (h[v] == h[b.v]) return w < b.w;//边长升序
        return h[v] > h[b.v];//高度降序
    }
} edge[MAX << 1];
int vis[MAX], fa[MAX];
ll cnt = 0, sum = 0;

void add_edge(int u, int v, int w) {
    tot++;
    edge[tot].u = u;
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot;
}
void bfs(int s) {
    queue<int> que; que.push(s);
    vis[s] = 1; cnt++;
    while (que.size()) {
        int u = que.front(); que.pop();
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (!vis[v]) {
                que.push(v);
                vis[v] = 1;
                cnt++;
            }
        }
    }
}

int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void kruskal() {
    sum = 0;
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= tot; i++) {
        int u = edge[i].u, v = edge[i].v, w = edge[i].w;
        if (vis[u] && vis[v])
            if (find(u) != find(v)) {
                fa[find(u)] = find(v);
                sum += w;
            }
    }
}

int main() {
    n = read(), m = read();
    for (int i = 1; i <= n; i++)    h[i] = read();
    for (int i = 1; i <= m; i++) {
        int u = read(), v = read(), w = read();
        if (h[u] >= h[v]) add_edge(u, v, w);
        if (h[v] >= h[u]) add_edge(v, u, w);
    }

    // 处理得到可以到达的节点
    bfs(1);
    sort(edge + 1, edge + 1 + tot);
    //按边权升序排序,保证每次取到最小边权的路径
    kruskal();
    printf("%lld %lld", cnt, sum);
    return 0;
}