计蒜客 圣诞树(堆优化dijkstra)

匿名 (未验证) 提交于 2019-12-02 23:26:52
版权声明:原创博客 喜欢拿走 https://blog.csdn.net/qq_43004519/article/details/88851807

题目描述

圣诞节快到了,蒜头君准备做一棵大圣诞树。

这棵树被表示成一组被编号的结点和一些边的集合,树的结点从 1 到 n 编号,树的根永远是 1。每个结点都有一个自身特有的数值,称为它的权重,各个结点的权重可能不同。对于一棵做完的树来说,每条边都有一个价值 ve,若设这条边 e 连接结点 i 和结点 j,且 i 为 j的父结点(根是最老的祖先),则该边的价值ve=sj*we,sj表示结点 j 的所有子孙及它自己的权重之和,we表示边 e 的权值。

现在蒜头君想造一棵树,他有 m 条边可以选择,使得树上所有边的总价值最小,并且所有的点都在树上,因为蒜头君喜欢大树。

输入格式

第一行输入两个整数 n 和 m(0≤n,m≤50,000),表示结点总数和可供选择的边数。
接下来输入一行,输入 n 个整数,依次表示每个结点的权重。
接下来输入 m 行,每行输入 3 个正整数a,b,c(1≤a,b,≤n,1≤c≤10,000),表示结点 a 和结点 b 之间有一条权值为 c 的边可供造树选择。

输出格式

输出一行,如果构造不出这样的树,请输出No Answer,否则输出一个整数,表示造树的最小价值。

样例输入

4 4
10 20 30 40
1 2 3
2 3 2
1 3 5
2 4 1

样例输出

370

简要分析

这道题原本是求每条边的价值之和,但是根据公式 Ve=Sj × We 分析一下,就可以推导出

最后的总价值之和 =(顶点① 的权值× 顶点①到根①的边总权值和) + (顶点②的权值 × 顶点②到根①的边总权值和) + … + (顶点n的权值 × 顶点n到根①的边总权值和)

各个顶点的权值都是定了的,因此本题也就转化成求顶点1到各个顶点的最短路径

ac代码

#include<iostream> #include<cstring> #include<cstdio> #include<set> using namespace std;  const int MAX=50001; int n,m; struct edge{     int v,w,next;     edge(){}     edge(int _v,int _w,int _n):v(_v),w(_w),next(_n){} }e[MAX*2]; int w[MAX],p[MAX],dist[MAX];//存储点的权值,各点指向边的头指针,1到各点的距离 bool vst[MAX];//判断各点是否拜访 typedef pair<int,int> PII; set<PII,less<PII> > min_heap;//存储点和点到1的距离的数据集的集合,并按照距离从小到大排序 bool dijkstra(int s){     memset(vst,0,sizeof(vst));     memset(dist,0x3f,sizeof(dist));     dist[s]=0;//第一个点初始化为未拜访     min_heap.insert(make_pair(0,s));//将起点放入集合     for(int i=0;i<n;i++){         if(min_heap.size()==0)             return false;         set<PII,less<PII> >::iterator iter=min_heap.begin();//取出集合中距离最小的点         int u=iter->second;//存储距离最小的点的编号         vst[u]=true;//标记为已访问         min_heap.erase(*iter);//擦除这个点         for(int j=p[u];j+1;j=e[j].next){//邻接表用法             int v=e[j].v;             if(!vst[v]&&dist[v]>dist[u]+e[j].w){//这个点未求出过最小距离,并且距离可更新                 min_heap.erase(make_pair(dist[v],v));//擦除原有数据                 dist[v]=dist[u]+e[j].w;//更新数据                 min_heap.insert(make_pair(dist[v],v));//放入新数据             }         }     }     return true; }  int main() {     scanf("%d%d",&n,&m);     for(int i=1;i<=n;i++)scanf("%d",&w[i]);     memset(p,-1,sizeof(p));     for(int i=0;i<2*m;){         int u,v,c;         scanf("%d%d%d",&u,&v,&c);         e[i]=edge(v,c,p[u]);         p[u]=i++;         e[i]=edge(u,c,p[v]);         p[v]=i++;     }     if(dijkstra(1)){         long long sum=0;         for(int i=1;i<=n;i++){             sum+=dist[i]*w[i];         }         cout<<sum<<endl;     }     else         cout<<"No Answer"<<endl;     return 0;//give me five }  
文章来源: https://blog.csdn.net/qq_43004519/article/details/88851807
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!