a180285 非常喜欢滑雪。他来到一座雪山,这里分布着 m m m 条供滑行的轨道和 n n n 个轨道之间的交点(同时也是景点),而且每个景点都有一编号 i ( 1 ≤ i ≤ n ) i\space (1 \le i \le n) i (1≤i≤n)和一高度 h i h_i hi。
a180285 能从景点 i i i 滑到景点 j j j 当且仅当存在一条 i i i 和 j j j 之间的边,且 i i i 的高度不小于 j j j。与其他滑雪爱好者不同,a180285 喜欢用最短的滑行路径去访问尽量多的景点。如果仅仅访问一条路径上的景点,他会觉得数量太少。
于是 a180285 拿出了他随身携带的时间胶囊。这是一种很神奇的药物,吃下之后可以立即回到上个经过的景点(不用移动也不被认为是 a180285 滑行的距离)。
请注意,这种神奇的药物是可以连续食用的,即能够回到较长时间之前到过的景点(比如上上个经过的景点和上上上个经过的景点)。 现在,a180285站在 1 1 1 号景点望着山下的目标,心潮澎湃。他十分想知道在不考虑时间胶囊消耗的情况下,以最短滑行距离滑到尽量多的景点的方案(即满足经过景点数最大的前提下使得滑行总距离最小)。你能帮他求出最短距离和景点数吗?
输入的第一行是两个整数 n , m n,m n,m。 接下来一行有 n n n 个整数 h i h_i hi ,分别表示每个景点的高度。
接下来 m m m 行,表示各个景点之间轨道分布的情况。每行三个整数 u , v , k u,v,k u,v,k,表示编号为 u u u 的景点和编号为 v v v 的景点之间有一条长度为 k k k 的轨道。
输出一行,表示 a180285 最多能到达多少个景点,以及此时最短的滑行距离总和。
3 3
3 2 1
1 2 1
2 3 1
1 3 10
3 2
对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 2000 1 \le n \le 2000 1≤n≤2000;
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1≤n≤105
对于所有的数据,保证 1 ≤ m ≤ 1 0 6 1 \le m \le 10^6 1≤m≤106 , 1 ≤ h i ≤ 1 0 9 1 \le h_i \le 10^9 1≤hi≤109, 1 ≤ k i ≤ 1 0 9 1 \le k_i \le 10^9 1≤ki≤109。
可以发现,整个景区可以抽象为一张图(明显废话),景点的高度为点权,景点间的距离为边权,所以这个长的像坨shit的题面就被我们翻译成了一句话:在只能从点权大的点到点权小的点(可以相等)的情况下,从1点出发建立一棵尽可能有更多点的最小生成树
在这种多条件题目中,我们有一个优先考虑顺序(反正我是这样的啦),即先考虑是否可行,再考虑最优,比如这道题,我们就先考虑一些硬性的规定:只能从高滑到低,再考虑景点多、距离小这样的条件
如果给你一张无向图,它已经满足每个景点都可以从1号景点按要求到达,那是不是这个点就解决了?那必须的呀,我们想要的就是这样一张图呀!所以现在考虑造出这样一张图。想想我们知道什么,只有每个景点的高度及每两个景点的连通性(距离我们暂时不需要),这就够了!对于每一条边,我们规定它的方向为从高的连向低的,让后从1号景点开始跑一遍BFS/DFS,所有能跑到的点以及经过的边组成一张新图,这张新图就是前面说的满足要求的图了
代码:
//tmp为记录能到的点数(题目要问)
//vis为记录是否以及遍历过这个点,防止不断递归
//g为原图
//g_new为新图
void dfs(int i){
if(vis[i])return;
tmp++;
vis[i]=true;
for(int j=0;j<g[i].size();j++){
g_new[++cnt].u=i;
g_new[cnt].v=g[i][j].u;
g_new[cnt].w=g[i][j].w;
dfs(g[i][j].u);
}
return;
}
要让到的景点最多,肯定是能到的点都要到,也就是说,我们在刚才DFS遍历的时候就应该存储一下能到的点的个数,直接输出。
这就完了?开始考虑总距离最短?错!能到的点一定可以到没问题,但是不能光考虑总距离,如果光考虑总距离最小,就有可能出现反着爬坡的情况(因为最小生成树是不会考虑边的方向的),那怎么办?
在对边排序的时候,按终点的高度从大到小进行排序,这样就能从高到低下来了
还有个总距离最短的条件,这个简单,在高度的基础上,以边权为第二关键字,从小到大排即可
代码
bool cmp(edge x,edge y){
if(hi[x.v]!=hi[y.v]){
return hi[x.v]>hi[y.v];
}
return x.w<y.w;
}
最后就是计算最小生成树了,直接用模板即可,代码就不单独放了,免得又被你们说是凑字数
代码打好了,愉快的交上去,错了!什么鬼,是我写错了?请注意,这道题坑点有亿点点多
我知道你们只喜欢这个
#include
#include
#include
#include
using namespace std;
const int MAXN=100005;
const int MAXM=2000005;
struct node{
int u;
int w;
node(){}
node(int U,int W){
u=U;
w=W;
}
};
struct edge{
int u,v;
int w;
}g_new[MAXM];
int hi[MAXN];
vector<node> g[MAXN];
bool vis[MAXN];
int cnt=0;
int tmp=0;
void dfs(int i){
if(vis[i])return;
tmp++;
vis[i]=true;
for(int j=0;j<g[i].size();j++){
g_new[++cnt].u=i;
g_new[cnt].v=g[i][j].u;
g_new[cnt].w=g[i][j].w;
dfs(g[i][j].u);
}
return;
}
int fa[MAXN];
int get(int x){
if(fa[x]==x){
return x;
}
return fa[x]=get(fa[x]);
}
bool cmp(edge x,edge y){
if(hi[x.v]!=hi[y.v]){
return hi[x.v]>hi[y.v];
}
return x.w<y.w;
}
long long kruskal(int m){
long long sum=0;
sort(g_new+1,g_new+1+m,cmp);
// for(int i=1;i<=m;i++){
// printf("%d %d %d\n",g_new[i].u,g_new[i].v,g_new[i].w);
// }
for(int i=1;i<=m;i++){
int fu=get(g_new[i].u);
int fv=get(g_new[i].v);
if(fu!=fv){
fa[fv]=fu;
sum+=g_new[i].w;
}
}
return sum;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&hi[i]);
}
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&u,&v,&w);
if(hi[u]<hi[v]){
swap(u,v);
}
g[u].push_back(node(v,w));
if(hi[u]==hi[v]){
g[v].push_back(node(u,w));
}
}
dfs(1);
for(int i=1;i<=n;i++){
fa[i]=i;
}
printf("%d %lld",tmp,kruskal(cnt));
return 0;
}
如果我的题解对你有帮助,帮我把下面那个惨白的竖着大拇指的手点红呗