[SCOI2012]滑雪与时间胶囊
一、题目
二、思路
这题好像kruskal和prim都能写
大概题意是从1号点出发,求能经过的最多点的最小生成树
因为不一定所有的点都能达到,所以先做一遍bfs把从1号点开始能达到的点标记上
然后对所有的边进行排序,按高度为第一关键字降序,边权第二关键字升序,这样就能保证在点最大的同时总边权最小
三、代码
#include<bits/stdc++.h>
using namespace std;
long long sum;
long long h[1000010];
int s[1000010];
bool vis[1000010];
int idx;
int head[1000010];
long long cnt;
long long n, m;
struct Edge{
int u; int v;
int w;
int next;
bool operator < (const Edge & b) const{
if(h[v] == h[b.v]) return w < b.w;
return h[v] > h[b.v];
}
}edges[2000020];
void add(int a, int b, int c){
idx ++;
edges[idx].u = a;
edges[idx].v = b;
edges[idx].w = c;
edges[idx].next = head[a];
head[a] = idx;
}
void bfs(int start){
memset(vis, 0, sizeof vis);
queue<int> q;
q.push(start);
vis[start] = 1;
cnt ++;
while(q.size()){
int u = q.front();
q.pop();
for(int i = head[u]; i != -1; i = edges[i].next){
int v = edges[i].v;
if(!vis[v]){
vis[v] = 1;
q.push(v);
cnt ++;
}
}
}
}
int find(int x){
if(s[x] != x) s[x] = find(s[x]); //这里记得路径压缩,不然会超时
return s[x];
}
void kruskal(){
sum = 0;
for(int i = 1; i <= n; i ++) s[i] = i;
for(int i = 1; i <= idx; i ++){
int u = edges[i].u, v = edges[i].v, w = edges[i].w;
if(vis[u] && vis[v]){ //这两个点在同一个点集合中
if(find(u) != find(v)){
s[find(u)] = find(v);
sum += w;
}
}
}
}
int main(){
cin >> n >> m;
memset(head, -1, sizeof head);
for(int i = 1; i <= n; i ++){
cin >> h[i];
}
int a, b, c;
for(int i = 1; i <= m; i ++){
scanf("%d%d%d", &a, &b, &c);
if(h[a] >= h[b]) add(a, b, c); //如果相等的话要连条双向的边,否则连一条从高到低的单项边
if(h[b] >= h[a]) add(b, a, c);
}
bfs(1);
sort(edges + 1, edges + 1 + idx);
kruskal();
cout << cnt << " " << sum << endl;
return 0;
}


浙公网安备 33010602011771号