题目:
a180285非常喜欢滑雪。他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1 ≤ i ≤ N)和一高度Hi。a180285 能从景点i 滑到景点j 当且仅当存在一条i 和j 之间的边,且i 的高度不小于j。
与其他滑雪爱好者不同,a180285喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。于是a180285拿出了他随身携带的时间胶囊。
这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间 之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。
现在,a180285站在1号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间 胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前 提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?
做法:
题目大意是给你一个有向图,求1所在联通块的最小树形图。
但是此题范围较大,朱刘算法行不通。
先从1出发bfs出联通块,得到1所在的有向图。
从题意中我们还得知,这个有向图不会有环,或者说不会有点数≥3的环。因为只能从高出往低处滑。但当时,u、v之间彼此可以到达。存在2元环。也可以说u,v之间是一条无向边。
那么这个问题怎么处理呢?
假如图中所有点的高度都相同。则图变成无向图了。跑最小生成树就行了,从小到大选边。而如果图有不同高度的点,我们考虑将点分层。一开始位于最高层的点必定形成一个无向图,对这些点做最小生成树。然后往下一层扩展时,将上层缩成一个点加入次高层中。此时次高层中有本层的无向边,也有上层下来的有向边。因为不存在自下往上的边,这些有向边可以当作无向边处理的。所以此时同样也是跑最小生成树。然后层层递推下去就能求出最小树形图了。
以上这个思维过程可以很简单实现。(神奇)
对边以高度为第一关键字从大到小(分层),边权为第二关键字从小到大(贪心选边)排序然后kruskal即可。
代码:
#include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false), cin.tie(0) #define debug(a) cout << #a ": " << a << endl using namespace std; typedef long long ll; const int N = 1e6 + 7; const int M = 2e6 + 7; int n, m, h[N]; struct edge{ int u, v, w, nxt; bool operator<(const edge& rh)const{ if (h[v] != h[rh.v]) return h[v] > h[rh.v]; return w < rh.w; } }e[M]; int tot, head[N]; int q[M], top, tail, vis[N]; int cnt, fa[N]; ll ans; void init(void){ tot = 0; memset(head, -1, sizeof head); } void addedge(int u, int v, int w){ e[tot] = (edge){u, v, w, head[u]}; head[u] = tot++; } void bfs(int s){ top = tail = cnt = 0; q[tail++] = s, vis[s] = 1, cnt++; while (top != tail){ int u = q[top++]; for (int i = head[u]; i != -1; i = e[i].nxt){ int v = e[i].v; if (!vis[v]){ q[tail++] = v, vis[v] = 1, cnt++; } } } } int getfa(int x){ return fa[x] == x ? x : fa[x] = getfa(fa[x]); } void kruskal(void){ ans = 0; for (int i = 1; i <= n; ++i) fa[i] = i; for (int i = 0; i < tot; ++i){ int u = e[i].u, v = e[i].v, w = e[i].w; if (vis[u] && vis[v]){ if (getfa(u) != getfa(v)){ fa[getfa(u)] = getfa(v); ans += w; } } } } int main(void){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i){ scanf("%d", &h[i]); } init(); for (int i = 0; i < m; ++i){ int u, v, w; scanf("%d%d%d", &u, &v, &w); if (h[u] >= h[v]) addedge(u, v, w); if (h[v] >= h[u]) addedge(v, u, w); } bfs(1); sort(e,e+tot); kruskal(); printf("%d %lld\n", cnt, ans); return 0; }