[SCOI2012]滑雪與時間膠囊
阿新 • • 發佈:2021-08-09
一、題目
二、思路
這題好像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; }