「SCOI2012」滑雪与时间胶囊(kruskal)

题目描述

a180285 非常喜欢滑雪。他来到一座雪山,这里分布着 m 条供滑行的轨道和 n 个轨道之间的交点(同时也是景点),而且每个景点都有一编号 i (1≤i≤n) 和一高度 h。

a180285 能从景点 i 滑到景点 j 当且仅当存在一条 i 和 j 之间的边,且 i 的高度不小于 j。与其他滑雪爱好者不同,a180285 喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。

于是 a180285 拿出了他随身携带的时间胶囊。这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。

请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。 现在,a180285站在 1 号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?

输入格式

输入的第一行是两个整数 n,m。 接下来一行有 n 个整数 h
i

,分别表示每个景点的高度。

接下来 m 行,表示各个景点之间轨道分布的情况。每行三个整数 u,v,k 表示编号为 uu的景点和编号为 v 的景点之间有一条长度为 k 的轨道。

输出格式

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

输入输出样例

输入 #1

3 3
3 2 1
1 2 1
2 3 1
1 3 10

输出 #1

3 2

分析

首先拿道题,觉得十分简单,随手写了一个近似模板的Kruskal的代码,嗯~~不错,WA 0了,于是开始仔细读题,
以最短滑行距离滑到尽量多的景点的方案。居然还要遍历的点最多,有点麻烦啊。于是,开始瞎搞。

满足高度限制

当两个顶点有边相连,找到高度大的顶点作为起点,高度小的顶点作为终点,边的长度作为距离,放进一个数组。

满足遍历的点最多

要使遍历的点最多,即得到从一开始包含最多点的图,可以写一个bfs,先将起点1压入队,然后把起点能直接到达的点继续压入,并将计数器加一下。直到已经不能找到其他可以达到的点。可以用一个结构体存储起点、终点、距离,并用一个二维vector存储结构体以便找到与之相连的点。设置一个结构体数组,当找到可以扩展的顶点时,将当时vector中的起点,终点,距离全部赋给数组,可以更好地进行下面的Kruskal。
综上所述,可以写出代码:

void bfs() {
	vis[1] = 1;
	jis[++numr] = 1;
	while(numl < numr) {
		ll f = jis[++numl];
		int Size = vec[f].size();
		for(int i = 0;i < Size;i ++) {
			a[++cnt].u = f;
			a[cnt].v = vec[f][i].v;
			a[cnt].w = vec[f][i].w;
			if(!vis[a[cnt].v]) {
				vis[a[cnt].v] = 1;
				tmp ++;
				jis[++numr] = a[cnt].v;
			}
		}
	}
}

排序

由于先要满足遍历的点最多,所以说当前边终点要从从大到小排序,这样可以满足高度不上升,也就可以满足能到达最多的点。在此情况下,高度已经不下降了,如果有两条边,终点高度相等,就需要距离尽量小,以满足距离在最多点的情况下最小这一条件。
代码:

friend bool operator<(node x, node y) {
        return (high[x.v] == high[y.v]) ? x.w < y.w : high[x.v] > high[y.v];
    }

Kruskal

emm…没什么别的,把模板贴上去就行了。

其他的几乎就没什么难的了,不过码量有点大,近2k,我调了一个晚自习(约3 hour).
终于又写()了一片题解,愉快的代码时间:

#include 
#include 
#include 
#include 
#include 
#define ll long long
using namespace std;
const ll MAX_NODE = 2e6 + 5;
const ll MAX_EDGE = 2e6 + 5;
ll n, m, high[MAX_NODE], cnt, numl, numr, jis[MAX_EDGE];
ll judge, fa[MAX_NODE], ans, tmp, head[MAX_EDGE];
bool vis[MAX_EDGE];
struct node {
    ll u, v, w;
    node() {}
    node(ll U, ll V, ll W) { u = U, v = V, w = W; }
    friend bool operator<(node x, node y) {
        return (high[x.v] == high[y.v]) ? x.w < y.w : high[x.v] > high[y.v];
    }
} a[MAX_EDGE];
struct data {
    long long v, w;
    data() {}
    data(int V, int W) { v = V, w = W; }
} b[MAX_EDGE];
vector<data> vec[MAX_EDGE];
ll Find_Set(ll x) {
    if (fa[x] == x)
        return x;
    return fa[x] = Find_Set(fa[x]);
}
void add(ll u, ll v, ll c) { vec[u].push_back(data(v, c)); }
void bfs() {
    vis[1] = 1;
    jis[++numr] = 1;
    while (numl < numr) {
        ll f = jis[++numl];
        int Size = vec[f].size();
        for (int i = 0; i < Size; i++) {
            a[++cnt].u = f;
            a[cnt].v = vec[f][i].v;
            a[cnt].w = vec[f][i].w;
            if (!vis[a[cnt].v]) {
                vis[a[cnt].v] = 1;
                tmp++;
                jis[++numr] = a[cnt].v;
            }
        }
    }
}
int main() {
    scanf("%lld %lld", &n, &m);
    for (ll i = 1; i <= n; i++) scanf("%lld", &high[i]);
    for (ll i = 1; i <= m; i++) {
        ll x, y, z;
        scanf("%lld %lld %lld", &x, &y, &z);
        if (high[x] <= high[y])
            add(y, x, z);
        if (high[y] <= high[x])
            add(x, y, z);
    }
    bfs();
    sort(a + 1, a + cnt + 1);
    for (ll i = 1; i <= n; i++) fa[i] = i;
    for (ll i = 1; i <= cnt; i++) {
        ll x = Find_Set(a[i].u);
        ll y = Find_Set(a[i].v);
        if (x == y)
            continue;
        fa[x] = y;
        ans += a[i].w;
    }
    printf("%lld %lld", tmp + 1, ans);
    return 0;
}

END

你可能感兴趣的:(生成树,图论)