1. 程式人生 > 其它 >[SCOI2012]滑雪與時間膠囊

[SCOI2012]滑雪與時間膠囊

一、題目

NC20568

二、思路

這題好像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;
}