题目大意:
一个孩子去滑雪,他想要用最短距离访问最多景点,题目会给出M条路径和N个景点。景点是路径的交点。这个孩子还有时间胶囊,时间胶囊可以让他直接转移到上次访问过的景点,问:他从第一个点开始滑,在访问最多景点的条件下,最短路径是多少?
这道题不能用 prim 算法去做,原来 prim 是有缺陷的,它针对无向图是正确的,并不能保证有向图一定正确。
比如说
3 3
3 2 1
1 2 7
2 3 1
1 3 5
这样的话用prim得出的结论是5+7=12,而实际上,我们可以先到2号点,再从二号到3号。这样答案就是最优解7+1=8;故若采用最小生成树方法时,便应当考虑kruskal算法。
思路是这样的:
先用bfs()遍历完所有可能遍历到的节点,标记能遍历到的,然后对原式进行排序先从去的高处向下排,再按路径长度按从小到大排序,然后就套用Kruskal模板。
为什么要用bfs()先遍历呢?这是因为或许会出现一种情况:一条边的两端不可以被遍历到,但是这条边很短,且终点很高,容易被Kruskal添加进去,故要避免这种情况。
先用bfs()遍历,能被遍历的节点被标记为need=true;添加边时应当使两端顶点need都为true;
#include<list>
#include<queue>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
struct place//景点结点
{
int from;
int to;
int disten;
};
struct rec//辅助bfs()
{
int to;
int weight;
rec* next;
};
rec* vec[100001];
const int inf = 0x3f3f3f3f;
bool need[100001];
bool used[100001];
int hei[100001];
place p[2000001];
int parent[100001];
int n, m;
int coun;//统计遍历的边数 [0,coun)
int reach;
void addedge(int f,int t,int w)
{
p[coun++]=place{f,t,w};
rec *l=new rec;
l->to=t;
l->weight=w;
l->next=vec[f];
vec[f]=l;
}
bool com(place a, place b)
{
if (hei[a.to] == hei[b.to])
return a.disten < b.disten;
else
return hei[a.to]>hei[b.to];
}
void bfs() //遍历所有可能到达的结点
{
queue<int>q;
q.push(1);
need[1]=true;
while(!q.empty())
{
int t=q.front();
q.pop();
if(++reach==n) return ;
for( rec* it=vec[t]; it; it=it->next)
{
if(need[it->to]==false)
{
need[it->to]=true;
q.push(it->to);
}
}
}
}
int getpare(int a)//parent【a】=getparent(parent【a】),这句话会很好的提高效率,加快访问到根节点的时间
{
return (parent[a] == a) ? a : parent[a]=getpare(parent[a]);
}
void kruskal()//最小生成树
{
long long dis = 0;
sort(p, p + coun, com);//排序的原则是去的最高点优先,然后是路径长度
for (int q = 1; q <= n; ++q)
parent[q] = q;
for (int i = 0; i<coun; ++i)
{
if (need[p[i].from]&&need[p[i].to])
{
int p1 = getpare(p[i].from);
int p2 = getpare(p[i].to);
if (p1 != p2)
{
dis += p[i].disten;
parent[getpare(p[i].from)]=getpare(p[i].to);
}
}
}
printf("%lld\n",dis);
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &hei[i]);
reach = 0;
coun = 0;
int f, t, w;
for (int i = 1; i <= m; ++i)
{
scanf("%d%d%d", &f, &t, &w);
if (hei[f] >= hei[t])
{
addedge(f,t,w);
}
if(hei[f]<=hei[t])
{
addedge(t,f,w);
}
}
bfs();
printf("%d ",reach);
kruskal();
}