题意:给定 个点高度,和
条边,以及
条边的权值然后问,从1号点能到多少个点,并且所有能到的点的权值求和最小是多少
题解:第一问:明显的广搜直接解决,记得标记下点
第二问:因为可以返回到上面的任意的点,所以我们每去一个点,可以直接到与他相连且最近的那个点过去,(越解释越乱......)
其实就是最小生成树
比如我们从1点的一条边出去,把能连的点全部连上之后,相当于是一个集合,然后再加入1点可以到的,还没有加入集合的点,因为可以回溯到原来的某一个时间点,所以直接找距离集合最近的点,先加入,...然后重复
时间复杂度:搜索是: ,最小生成树是:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
#define MAXN 100001
#define MAXE 2000001
struct EDGE{int from,to,value,height,next;};
EDGE a[MAXE];
int N,M,tot=0,last[MAXN];
bool vis[MAXN];
int Q[MAXE],ans1,fa[MAXN],H[MAXN];
long long ans2=0LL;
bool cmp(EDGE X,EDGE Y)
{
if(X.height==Y.height) return X.value<Y.value;
return X.height>Y.height;
}
int get(int x){return fa[x]==x ? x : fa[x]=get(fa[x]);}
void add(int from,int to,int value)
{
a[++tot].from=from;
a[tot].to=to;
a[tot].value=value;
a[tot].height=H[to];
a[tot].next=last[from];
last[from]=tot;
}
void bfs()
{
int right=1;
Q[1]=1;ans1=1;
vis[1]=true;
for(int i=1;i<=right;i++)
{
int now=Q[i];
for(int j=last[now];j;j=a[j].next)
{
if(!vis[a[j].to])
{
vis[a[j].to]=true;
ans1++;
Q[++right]=a[j].to;
}
}
}
}
void kruskal()
{
int tot1=0;
sort(a+1,a+1+tot,cmp);
for(int i=1;i<=N;i++) fa[i]=i;
for(int i=1;i<=tot;i++)
{
if(!(vis[a[i].from] && vis[a[i].to])) continue;
int fx=get(fa[a[i].from]);
int fy=get(fa[a[i].to]);
if(fx!=fy)
{
fa[fy]=fx;
tot1++;
ans2+=(long long)a[i].value;
}
if(tot1==ans1-1) break;
}
}
int main()
{
scanf("%d%d",&N,&M);
for(int i=1;i<=N;i++)
scanf("%d",&H[i]);
for(int i=1;i<=M;i++)
{
int A,B,C;
scanf("%d%d%d",&A,&B,&C);
if(H[A]>=H[B]) add(A,B,C);
if(H[A]<=H[B]) add(B,A,C);
}
bfs();
kruskal();
printf("%d %lld\n",ans1,ans2);
return 0;
}
京公网安备 11010502036488号