NC20568 [SCOI2012] 滑雪与时间胶囊
题目链接
关键点:
1、题目要求求出在访问的景点最多的情况下,所能花费的最短距离和到达的最多的景点数量。
2、对于求能访问的最多景点,题目定的起点为1,那么就直接从1点开始dfs,看其所能到达的边,然后将其记录下来,并记录总个数。总个数即为访问的最多的景点数。
3、先考虑在所有点均为双向边的连接情况下,所能到达的最短距离,我们可以用最小生成树prim或者Kruskal算法来求解,我们这里考虑使用Kruskal来计算,但是这里有一个限制条件,即边有可能为单向的,如果我们直接对边的长度进行排序后,然后找最小边插入,那么我们有可能会造成该树的所有结点并不能均到达,即不满足在点最多的情况下找距离最小的情况。
4、因此我们要先挑那些高的景点(一条边的两点,那么该边的高度即为两点中高度较低的那个高度),在高度均相同的情况下再挑距离小的边,这样排序来选边
5、还有该边的两点要保证x能够到达,即在dfs中被标记的点
6、根据边的长度范围和边的数量,最短距离可能会很大,因此答案要开long long
完整代码:
# include <bits/stdc++.h>
using namespace std;
struct ty2{
int x, y, h, len;
}bian[1000000+10];
int n, m, cnt, num;
int fa[100000+10], h[100000+10], vis[100000+10], head[100000+10];
struct ty{
int t, next, len;
}edge[2000000+10];
void addedge(int x, int y, int len)
{
edge[++num].t = y;
edge[num].len = len;
edge[num].next = head[x];
head[x] = num;
}
void dfs(int x)
{
vis[x] = 1;
cnt++;
for (int i=head[x]; i!=-1; i = edge[i].next)
{
int y = edge[i].t;
if (vis[y]) continue;
dfs(y);
}
}
int find(int x)
{
return fa[x]==x? x: fa[x] = find(fa[x]);
}
bool cmp(ty2 t1, ty2 t2)
{
if (t1.h == t2.h) return t1.len<t2.len;
else
return t1.h>t2.h;
}
int main()
{
memset(head, -1, sizeof(head));
cin>>n>>m;
for (int i=1; i<=n; i++)
cin>>h[i];
for (int i=1; i<=m; i++)
{
int x, y, len;
cin>>x>>y>>len;
bian[i].x = x;
bian[i].y = y;
bian[i].len = len;
bian[i].h = min(h[x], h[y]);
if (h[x]>=h[y])
addedge(x, y, len);
if (h[y]>=h[x])
addedge(y, x, len);
}
dfs(1);
sort(bian+1, bian+1+m, cmp);
long long ans = 0;
for (int i=1; i<=n; i++) fa[i] = i;
for (int i=1; i<=m; i++)
{
int x = bian[i].x, y = bian[i].y;
if (!vis[x] || !vis[y]) continue;
int fx = find(x), fy = find(y);
if (fx!=fy)
{
ans += bian[i].len;
fa[fx] = fy;
}
}
cout<<cnt<<" "<<ans<<endl;
return 0;
}