题意:要建一棵圣诞树,使得总的花费最小。具体规则是:圣诞树是一颗无向树形图,其中,编号为1的节点为根节点,原始图中每条边具有边权:材料的单位价值;每个点也有一个权:点的重量。生成树中,各条边的花费是该边权* 该边的子树中所有点的重量和,总的花费则是生成树中所有边的花费之和。
题目要求是这样一棵树,每条边的权值是该边和所有子节点的点权和的乘积,那么,将我们的角度从边转移到点,对于每一个点,他所涉及的权值就是他延一条边到达源点1的距离*它本身的权值,对每个点都是如此;所以求出点1到每个点的单源最短路,每个点的距离乘点权和就是答案。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; #define inf 1LL<<61 #define ll long long const int maxn = 50050; const int maxm = 500020; struct Edge{ int v,w,next; }edge[maxm]; int E,head[maxn]; inline void init() { E=0; memset(head,-1,sizeof(head)); } inline void addedge(int u,int v,int w) { edge[E].v=v;edge[E].w=w;edge[E].next=head[u];head[u]=E++; edge[E].v=u;edge[E].w=w;edge[E].next=head[v];head[v]=E++; } queue<int> Q; bool mark[maxn]; ll dist[maxn]; int inque[maxn]; int n , m; void spfa(int s){ memset(mark,0,sizeof(mark)); memset(inque,0,sizeof(inque)); for(int i=1;i<=n;i++) dist[i]=inf; dist[s]=0; mark[s]=true; //inque[s]++; Q.push(s); int u,v,w; while(!Q.empty()){ u=Q.front();Q.pop(); mark[u]=false; for(int i=head[u];i!=-1;i=edge[i].next){ v=edge[i].v;w=edge[i].w; if(dist[u]+w<dist[v]){ dist[v]=dist[u]+w; if(!mark[v]){ mark[v]=true; Q.push(v); } } } } } int weight[maxn]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(int i=1;i<=n;i++) scanf("%d",&weight[i]); while(m--) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } spfa(1); ll ret = 0; for(int i=1;i<=n;i++) { if(dist[i]==inf) { ret = -1; break; } ret += weight[i] * dist[i]; } if(ret == -1) printf("No Answer\n"); else cout<<ret<<endl; } return 0; }