基础原理:特殊的生成树
给定一张无向图,其中边权都是正数,你需要求出总代价最小的生成树,生成树上每条边 (u,v)(u,v) 的代价为 w(u,v)∗count(v)w(u,v)∗count(v),其中 w(u,v)w(u,v) 为边 (u,v)(u,v) 的权值,count(v)count(v) 是 vv 所在子树的结点数总和。
解法:虽然看起来是一道生成树的题,实际上却是一道单源最短路的题目。我们把整棵树的代价加起来,实际上就等于从根结点出发到每个顶点的最短路之和。因此,从每个顶点出发求一遍单源最短路,取其中最短路总和最小的一个顶点作为根,其到所有顶点的单源最短路的总和就是最终答案。
但这题还给结点加上了权值,我们在计算结点的贡献值时需要乘上结点的权值(其实我没搞懂为啥直接乘就行了,不过推几个样例就能得到这个结果)。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n, m; 4 long long maximum = 0x3f3f3f3f; 5 long long res = 0; 6 typedef pair<int, int> pii; 7 8 vector<pii> graph[50005]; 9 vector<int> weight(50005, 0); 10 int dis[50005]; 11 set<pii, less<pii> > minHeap; 12 bool visited[50005]; 13 14 bool dijkstra(int p) { 15 memset(visited, false, sizeof(visited)); 16 memset(dis, 0x3f, sizeof(dis)); 17 minHeap.clear(); 18 minHeap.insert(make_pair(0, p)); 19 dis[p] = 0; 20 visited[p] = true; 21 22 while(!minHeap.empty()) { 23 auto node = minHeap.begin(); 24 int root = node -> second; 25 minHeap.erase(node); 26 visited[root] = true; 27 if (dis[root] >= maximum) { //存在无法到达的结点 28 return false; 29 } 30 res += dis[root] * weight[root]; //计算总价值 31 32 for (auto iter : graph[root]) { 33 if (!visited[iter.first]) { 34 int child = iter.first; 35 int value = iter.second; 36 if (value + dis[root] < dis[child]) { 37 minHeap.erase(make_pair(dis[child], child)); 38 dis[child] = dis[root] + value; 39 minHeap.insert(make_pair(dis[child], child)); 40 } 41 } 42 } 43 } 44 return true; 45 } 46 int main() { 47 cin >> n >> m; 48 for (int i = 1; i <= n; ++ i) { 49 scanf("%d", &weight[i]); 50 } 51 int a, b, c; 52 for (int i = 0; i < m; ++ i) { 53 cin >> a >> b >> c; 54 graph[a].push_back(make_pair(b, c)); 55 graph[b].push_back(make_pair(a, c)); 56 } 57 if (dijkstra(1)) { //树根固定为1 58 cout << res; 59 } else { 60 cout << "No Answer"; 61 } 62 63 }
标签:int,root,child,visited,dijkstra,圣诞树,minHeap,计蒜客,dis From: https://www.cnblogs.com/coderhrz/p/18542882