由于有向图的特殊性,因此不能每次简单判断最短边加入,否则可能出现以下情况:
假设在上图中,在已经连通了
(
1
,
3
,
4
)
(1,3,4)
(1,3,4) 三个点的情况下,边
(
2
,
4
)
(2,4)
(2,4) 比边
(
1
,
2
)
(1,2)
(1,2) 更短,则此时将加入边
(
2
,
4
)
(2,4)
(2,4) ,形成上图。然而由于边的有向性(即题面中的高度限制),此时的点
2
2
2 其实是到达不了的,必须需要通过边
(
1
,
2
)
(1,2)
(1,2) 进行连通。因此对于有向图的最小生成树,在加入某条边时还需要判断该边的始点是否已经被加入。
有关有向图的最小生成树,即最小树形图,其实可以通过朱刘算法解决(当然了,我不会 )。仔细阅读题面后发现,可以贪心的选择高度最高且路径最短的点加入,发现其他选择都不会比该种选法更优。
本题采用了 P r i m Prim Prim 算法,显然 K r u s k a l Kruskal Kruskal 算法也是可行的(如果以后有时间补上的话)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
const int M=2e6+10;
const int INF=0x3f3f3f3f;
vector<pair<int,int>> h[N];
int ans1,ans2;
bool vis[N];
int dis[N],high[N];
struct Node{
int numble,h,d;
bool operator <(const Node &t) const{
if(t.h!=h)
return h<t.h;
return d>t.d;
}
}no;
priority_queue<Node> q;
void prim(){
no.numble=1,no.h=high[1],no.d=0;
dis[1]=0;
q.push(no);
while(!q.empty()){
int x=q.top().numble;
q.pop();
if(vis[x])
continue;
if(dis[x]>=INF)
return;
ans1++;
ans2+=dis[x];
vis[x]=1;
for(auto i:h[x]){
int y=i.first;
int val=i.second;
if(dis[y]>val&&!vis[y]){
dis[y]=val;
no.numble=y,no.h=high[y],no.d=dis[y];
q.push(no);
}
}
}
}
signed main(){
memset(dis,0x3f,sizeof(dis));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>high[i];
int u,v,k;
for(int i=1;i<=m;i++){
cin>>u>>v>>k;
//建边
if(high[u]>high[v])
h[u].push_back({v,k});
else if(high[v]>high[u])
h[v].push_back({u,k});
else{
h[v].push_back({u,k});
h[u].push_back({v,k});
}
}
prim();
cout<<ans1<<" "<<ans2;
return 0;
}