[SCOI2012]滑雪 (最小生成树 Kruskal)
题目描述
a180285非常喜欢滑雪。他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有一编号i(1≤i≤N)和一高度Hi。a180285能从景点ii滑到景点j当且仅当存在一条i和j之间的边,且i的高度不小于j。 与其他滑雪爱好者不同,a180285喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。于是a180285拿出了他随身携带的时间胶囊。这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是a180285 滑行的距离)。请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。 现在,a180285站在11号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?
输入格式:
输入的第一行是两个整数N,M。
接下来1行有N个整数Hi,分别表示每个景点的高度。
接下来M行,表示各个景点之间轨道分布的情况。每行3个整数Ui,Vi,Ki。表示编号为Ui的景点和编号为Vi的景点之间有一条长度为Ki的轨道。
输出格式:
输出一行,表示a180285最多能到达多少个景点,以及此时最短的滑行距离总和。
题解:
对于高度不同的两点,他们之间的边是有向的,对于同于高度的点,有边就可以互相到达,我们只需要选取最短的一些边使他们仍联通即可。
若按照正常思路,以边权排序,直接跑Kruskal的话,会发现可能出现有下面的点连接上面的点,如:他会直接选取两条边权为5的边
我们考虑如何消除高度的影响,我们以终点高度为第一关键字,边权为第二关键字排序,那么就不可能出现上面的情况,因为他会先选20的边。
相当于选点是逐层进行的,上面的点一定会先被选取,所以这个点选了之后,不可能再连一条向上的边。
接着愉快地打出了代码。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=1000005; int n,m; ll ans; int fa[maxn],h[maxn]; struct edge{ int x,y; ll k; }a[maxn]; template<class T>inline void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } void swap(ll &x,ll &y){ll t=x;x=y;y=t;} bool cmp(edge a,edge b){ if(h[a.y]==h[b.y]) return a.k<b.k; return h[a.y]>h[b.y]; } int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void kruskal(){ sort(a+1,a+m+1,cmp); int k=0; for(int i=1;i<=m;i++){ int dx=find(a[i].x),dy=find(a[i].y); if(dx!=dy){ fa[dx]=dy; ans+=a[i].k; k++; } if(k==n-1) break;; } printf("%d %lld",k+1,ans); } int main(){ read(n);read(m); for(int i=1;i<=n;i++) read(h[i]),fa[i]=i; for(int i=1;i<=m;i++){ int x,y,z; read(x);read(y);read(z); if(h[x]<h[y]) swap(x,y); a[i]=(edge){x,y,z}; } kruskal(); }
然后。。。。。
我们会发现是什么没考虑到呢?接着便可以发现可能有出发点达不到的地方,这些地方是没用的。
那么就可以考虑记录1可以利用的边,用这些边跑Kruskal。在最初读入时对于高度不同的点建有向边,
高度相同建双向边,跑一边dfs,要判断是否遍历过。
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=1000005; int n,m,_n=1,cnt; ll ans; int fa[maxn],h[maxn]; bool vis[maxn]; vector<pair<int,ll> >e[maxn]; struct edge{ int x,y; ll k; }a[maxn]; template<class T>inline void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } void swap(ll &x,ll &y){ll t=x;x=y;y=t;} bool cmp(edge a,edge b){ if(h[a.y]==h[b.y]) return a.k<b.k; return h[a.y]>h[b.y]; } int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void kruskal(){ sort(a+1,a+cnt+1,cmp); int k=0; for(int i=1;i<=cnt;i++){ int dx=find(a[i].x),dy=find(a[i].y); if(dx!=dy){ fa[dx]=dy; ans+=a[i].k; k++; } if(k==_n-1) break; } printf("%d %lld",_n,ans); } void dfs(int u,int f){ for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i].first; if(v==f) continue; a[++cnt]=(edge){u,v,e[u][i].second}; if(!vis[v]){ vis[v]=true;_n++; dfs(v,u); } } } int main(){ read(n);read(m); for(int i=1;i<=n;i++) read(h[i]),fa[i]=i; for(int i=1;i<=m;i++){ int x,y,z; read(x);read(y);read(z); if(h[x]<h[y]) swap(x,y); e[x].push_back(make_pair(y,z)); if(h[x]==h[y]) e[y].push_back(make_pair(x,z)); } vis[1]=true; dfs(1,0); kruskal(); }