zoukankan      html  css  js  c++  java
  • [SCOI2012]滑雪 (最小生成树 Kruskal)

    题目描述

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

    输入格式:

    输入的第一行是两个整数N,M。

    接下来1行有N个整数Hi,分别表示每个景点的高度。

    接下来M行,表示各个景点之间轨道分布的情况。每行3个整数Ui,Vi,Ki。表示编号为Ui的景点和编号为Vi的景点之间有一条长度为Ki的轨道。

    输出格式:

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

    题解:

    对于高度不同的两点,他们之间的边是有向的,对于同于高度的点,有边就可以互相到达,我们只需要选取最短的一些边使他们仍联通即可。

    若按照正常思路,以边权排序,直接跑Kruskal的话,会发现可能出现有下面的点连接上面的点,如:他会直接选取两条边权为5的边

     

     

     我们考虑如何消除高度的影响,我们以终点高度为第一关键字,边权为第二关键字排序,那么就不可能出现上面的情况,因为他会先选20的边。

    相当于选点是逐层进行的,上面的点一定会先被选取,所以这个点选了之后,不可能再连一条向上的边。

    接着愉快地打出了代码。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    const int maxn=1000005;
    int n,m;
    ll ans;
    int fa[maxn],h[maxn];
    struct edge{
        int x,y;
        ll k;
    }a[maxn];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
    
    bool cmp(edge a,edge b){
        if(h[a.y]==h[b.y]) return a.k<b.k;
        return h[a.y]>h[b.y];
    }
    
    int find(int x){
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }
    
    void kruskal(){
        sort(a+1,a+m+1,cmp);
        int k=0;
        for(int i=1;i<=m;i++){
            int dx=find(a[i].x),dy=find(a[i].y);
            if(dx!=dy){
                fa[dx]=dy;
                ans+=a[i].k;
                k++;
            }
            if(k==n-1) break;;
        }
        printf("%d %lld",k+1,ans);
    }
    
    int main(){
        read(n);read(m);
        for(int i=1;i<=n;i++) read(h[i]),fa[i]=i;
        for(int i=1;i<=m;i++){
            int x,y,z;
            read(x);read(y);read(z);
            if(h[x]<h[y]) swap(x,y);
            a[i]=(edge){x,y,z};
        }
        kruskal();
    }
    View Code

    然后。。。。。

    我们会发现是什么没考虑到呢?接着便可以发现可能有出发点达不到的地方,这些地方是没用的。

    那么就可以考虑记录1可以利用的边,用这些边跑Kruskal。在最初读入时对于高度不同的点建有向边

    高度相同建双向边,跑一边dfs,要判断是否遍历过

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    
    const int maxn=1000005;
    int n,m,_n=1,cnt;
    ll ans;
    int fa[maxn],h[maxn];
    bool vis[maxn];
    vector<pair<int,ll> >e[maxn];
    struct edge{
        int x,y;
        ll k;
    }a[maxn];
    
    template<class T>inline void read(T &x){
        x=0;char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    }
    
    void swap(ll &x,ll &y){ll t=x;x=y;y=t;}
    
    bool cmp(edge a,edge b){
        if(h[a.y]==h[b.y]) return a.k<b.k;
        return h[a.y]>h[b.y];
    }
    
    int find(int x){
        if(fa[x]==x) return x;
        return fa[x]=find(fa[x]);
    }
    
    void kruskal(){
        sort(a+1,a+cnt+1,cmp);
        int k=0;
        for(int i=1;i<=cnt;i++){
            int dx=find(a[i].x),dy=find(a[i].y);
            if(dx!=dy){
                fa[dx]=dy;
                ans+=a[i].k;
                k++;
            }
            if(k==_n-1) break;
        }
        printf("%d %lld",_n,ans);
    }
    
    void dfs(int u,int f){
        for(unsigned int i=0;i<e[u].size();i++){
            int v=e[u][i].first;
            if(v==f) continue;
            a[++cnt]=(edge){u,v,e[u][i].second};
            if(!vis[v]){
                vis[v]=true;_n++;
                dfs(v,u);
            }
        }
    }
    
    int main(){
        read(n);read(m);
        for(int i=1;i<=n;i++) read(h[i]),fa[i]=i;
        for(int i=1;i<=m;i++){
            int x,y,z;
            read(x);read(y);read(z);
            if(h[x]<h[y]) swap(x,y);
            e[x].push_back(make_pair(y,z));
            if(h[x]==h[y]) e[y].push_back(make_pair(x,z));
        }
        vis[1]=true;
        dfs(1,0);
        kruskal();
    }
    View Code
  • 相关阅读:
    js事件分类
    过3s弹出广告条,点叉号关闭
    js进阶
    js入门
    html
    R Markdown + Infinite Moon Reader + 编辑实时更新
    png转ico+windows图标+GIMP
    微生物+计算细胞倍增时间
    使用Mathjax网页插入公式
    Firefox+zoom+全局缩放比例
  • 原文地址:https://www.cnblogs.com/sto324/p/11172209.html
Copyright © 2011-2022 走看看