POJ3013 Big Christmas Tree(最短路径树)
题目大概说给一张点和边都有权的图,现在要求其一棵以1结点为根的生成树使树的边权和最小,树边权 = 对应的图边权 * 树边末端点为根的子树所有结点对于图顶点的点权和。
要求∑(边权*子树点权和),等价于求∑(点权*点到根路径上的边权和)。
而如果在图中各个点到根都存在最短路,那么最短路的边一定能构造出一棵生成树。一个构造方法就是在进行最短路的松弛操作时记录各点是从哪点转移的,而各点的这个前驱是唯一的这就对应着各点的父结点。这就是最短路径树。
那么这一题直接求各个点到1点的最短路,最后乘一下加一下就OK了。
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<algorithm> 5 using namespace std; 6 #define INF (1LL<<60) 7 #define MAXN 55555 8 #define MAXM 55555*2 9 10 struct Edge{ 11 int v,w,next; 12 }edge[MAXM]; 13 int NE,head[MAXN]; 14 void addEdge(int u,int v,int w){ 15 edge[NE].v=v; edge[NE].w=w; edge[NE].next=head[u]; 16 head[u]=NE++; 17 } 18 19 long long d[MAXN]; 20 bool vis[MAXN]; 21 bool SPFA(int n){ 22 for(int i=1; i<=n; ++i){ 23 d[i]=INF; vis[i]=0; 24 } 25 d[1]=0; vis[1]=1; 26 queue<int> que; 27 que.push(1); 28 while(!que.empty()){ 29 int u=que.front(); que.pop(); 30 for(int i=head[u]; i!=-1; i=edge[i].next){ 31 int v=edge[i].v; 32 if(d[v]>d[u]+edge[i].w){ 33 d[v]=d[u]+edge[i].w; 34 if(!vis[v]){ 35 vis[v]=1; 36 que.push(v); 37 } 38 } 39 } 40 vis[u]=0; 41 } 42 for(int i=1; i<=n; ++i){ 43 if(d[i]==INF) return 0; 44 } 45 return 1; 46 } 47 48 int weight[MAXN]; 49 int main(){ 50 int t,n,m,a,b,c; 51 scanf("%d",&t); 52 while(t--){ 53 scanf("%d%d",&n,&m); 54 for(int i=1; i<=n; ++i){ 55 scanf("%d",weight+i); 56 } 57 NE=0; 58 memset(head,-1,sizeof(head)); 59 while(m--){ 60 scanf("%d%d%d",&a,&b,&c); 61 addEdge(a,b,c); 62 addEdge(b,a,c); 63 } 64 if(!SPFA(n)){ 65 puts("No Answer"); 66 continue; 67 } 68 long long ans=0; 69 for(int i=1; i<=n; ++i){ 70 ans+=d[i]*weight[i]; 71 } 72 printf("%lld\n",ans); 73 } 74 return 0; 75 }