滑雪与时间胶囊

[SCOI2012]滑雪与时间胶囊

https://ac.nowcoder.com/acm/problem/20568

题意

给定n个点,m条路径。每个点有个高度h,i可以到j,当且仅当h[i] >= h[j],i和j之间有路径。求从1出发,最多可以经过几个点,其最短路径长度。(使用时间胶囊可以回到之前经过的点)

思路

第一问简单bfs一下就行。第二问如果没有h的限制就是一个最小生成树,然而有高度的限制就不太行了。进一步分析好像是求一个有向图的最小生成树(大雾)。我们再来看看kruskal算法,每次都是贪心的选取最短的边,因为这里有高度的限制,每条从1出发的路径高度都是非递增的,我们显然不能这么做。那我们可不可以把这个限制“去掉”呢?好像是可以的,对于 从大到小排序,再按路径长度从小到大排,每次选的都是最高点,因此对于之后的选择高度就没有限制啦。

代码

#include<bits/stdc++.h>
using namespace std;


const int maxn = 1e5 + 10;
const int maxm = 1e6 + 10;

int n, m;
bool vis[maxn];
int f[maxn], h[maxn];

void I(int x){for(int i = 0; i <= x; i++) f[i] = i;}
int F(int x){return f[x] == x ? x : f[x] = F(f[x]);}
void U(int x, int y){f[F(x)] = F(y);}
bool S(int x, int y){return F(x) == F(y);}

struct Edge{
    int u, v;
    long long w;

    bool friend operator <(Edge a, Edge b){
        if(h[a.v] != h[b.v])
            return h[a.v] > h[b.v];
        return a.w < b.w;
    }
};


vector<int> g[maxn];
Edge e[maxm];
vector<Edge> E;


long long ans1, ans2;
void dfs(int i){
    vis[i] = 1, ans1++;
    for(int v : g[i]){
        if(vis[v] || (h[i] < h[v])) continue;
        dfs(v);
    }
}

void Kruskal(){
    long long cnt = 0;
    sort(E.begin(), E.end());
    for(auto it : E){
        if(!S(it.u, it.v)){
            U(it.u, it.v);
            ans2 += it.w;
            cnt ++;
        }

        if(cnt + 1 == ans1) break;
    }

}

int main()
{
    cin >> n >> m;
    I(n);
    for(int i = 1; i <= n; i++){
        cin >> h[i];
    }
    for(int i = 0; i < m; i++){
        scanf("%d %d %lld", &e[i].u, &e[i].v, &e[i].w);
        //cin >> e[i].u >> e[i].v >> e[i].w;  cin会超,亲测~~
        int u = e[i].u, v = e[i].v;
        g[u].push_back(v);
        g[v].push_back(u);
    }

    dfs(1);
    for(int i = 0; i < m; i++){
        int u = e[i].u, v = e[i].v, w = e[i].w;

        if(vis[u] && vis[v]){
            if(h[u] >= h[v]){
                E.push_back({u, v, w});
            }
            if(h[u] <= h[v]){
                E.push_back({v, u, w});
            }
        }
    }
    Kruskal();
    cout << ans1 << " " << ans2 << endl;

    return 0;
}
全部评论

相关推荐

行云流水1971:这份实习简历的优化建议: 结构清晰化:拆分 “校园经历”“实习经历” 板块(当前内容混杂),按 “实习→校园→技能” 逻辑排版,求职意向明确为具体岗位(如 “市场 / 运营实习生”)。 经历具象化:现有描述偏流程,需补充 “动作 + 数据”,比如校园活动 “负责宣传” 可加 “运营公众号发布 5 篇推文,阅读量超 2000+,带动 300 + 人参与”;实习内容补充 “协助完成 XX 任务,效率提升 X%”。 岗位匹配度:锚定目标岗位能力,比如申请运营岗,突出 “内容编辑、活动执行” 相关动作;申请市场岗,强化 “资源对接、数据统计” 细节。 信息精简:删减冗余表述(如重复的 “负责”),用短句分点,比如 “策划校园招聘会:联系 10 + 企业,组织 200 + 学生参与,到场率达 85%”。 技能落地:将 “Office、PS” 绑定经历,比如 “用 Excel 整理活动数据,输出 3 份分析表;用 PS 设计 2 张活动海报”,避免技能单独罗列。 优化后需强化 “经历 - 能力 - 岗位需求” 的关联,让实习 / 校园经历的价值更直观。 若需要进一步优化服务,私信
实习,投递多份简历没人回...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务