题意:要建一棵圣诞树,使得总的花费最小。具体规则是:圣诞树是一颗无向树形图,其中,编号为1的节点为根节点,原始图中每条边具有边权(unit):材料的单位价值;每个点也有一个权(weight):点的重量。生成树中,各条边的花费是该边权(unit)* 该边的子树中所有点的重量(weight)和,总的花费则是生成树中所有边的花费之和。
分析:1、在树中,两点的路径是唯一的
2、对点u,只有从点u到根结点之间的边会乘以点u的重量
3、点u的重量不变
结论:最小总花费=每条边(u,v)*v的子树中各结点的重量
=每个点的重量*从该点到根结点的每条边的单位价值
=每个点*该点到根结点的最短路
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn=50000+5; const long long INF=0x3f3f3f3f3f; int weight[maxn]; int cnt; int head[maxn]; long long dis[maxn]; bool vis[maxn]; int n,m; struct Edge { int to,w,next; } edge[maxn<<1]; struct Node { int u,dis; // 此处的dis仅仅是为了使用优先队列:定义优先级 bool operator <(const Node &a) const { return dis>a.dis; // 当返回true时会更新堆,因此当新元素a的dis小于堆顶元素dis // 的时候会返回true,同时会更新堆,故此堆为小顶堆 } }; void Init() { cnt=0; memset(head,-1,sizeof(head)); memset(vis,false,sizeof(vis)); for(int i=0; i<=n; i++) dis[i]=INF; } void addEdge(int u,int v,int w) { edge[cnt].to=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++; } void Dijkstra(int s) { Node now,next; priority_queue<Node>q; now.dis=0; now.u=s; dis[s]=0; q.push(now); while(!q.empty()) { now=q.top(); q.pop(); if(vis[now.u]) continue; int u=now.u; vis[u]=true; for(int i=head[u]; i!=-1; i=edge[i].next) { int to=edge[i].to; //对点v,会有多个dis存在,不过我们只取对顶元素, //即dis最小的点v if(!vis[to]&&dis[u]+edge[i].w<dis[to]) { dis[to]=dis[u]+edge[i].w; next.dis=dis[to]; next.u=to; q.push(next); } } } } int main() { int T; cin>>T; while(T--) { cin>>n>>m; Init(); for(int i=1; i<=n; i++) scanf("%d",&weight[i]); int u,v,w; for(int i=1; i<=m; i++) { scanf("%d%d%d",&u,&v,&w); addEdge(u,v,w); addEdge(v,u,w); } Dijkstra(1); long long sum=0; bool flag=true; for(int i=2; i<=n; i++) { if(dis[i]==INF) { flag=false; break; } sum+=dis[i]*weight[i]; } if(flag) cout<<sum<<endl; else cout<<"No Answer"<<endl; } return 0; }