这道题目咋一看就是最小生成树啊。
题目描述大概是这样:
KCm要准备一颗圣诞树,这棵树有一些节点和边组成。节点从1到n,根总是1.每个节点都有自己的总量,而边的价格是由边的单价乘以子孙节点的重量。
求出这么一颗有n个节点的树,使花费最小。
很像最小生成树,仔细研究,没法这样做。因为每条边在找到这颗树之前很难计算它的价值。
因为本来你算好的边的价值,但是在去掉某些边时,会导致边的价值发生变化,那怎么办。。。。
这道题目开始我也是不知道怎么计算,看到discuss说是最短路。试想最后已经得到一个树,而每条边的价值就是子孙节点重量和乘以单价,可以转换为,此节点重量乘以从根到本节点的最短路,这么一转化,问题瞬间变得简单了。
那么所有点的最短路径一定是一棵树吗? 这个是显然的 ,因为如果形成环了,到达某个点就出现了多个路径,就要删去长的那条,使其没有环
样例中的计算方法
4*40+3*50+2*60+3*(20+40+50+60)+2*30+1*(10+20+30+40+50+60)
=10*1+20*(1+3)+30*(2+1)+40*(4+1+3)+50*(3+1+3)+60*(1+2+3)
=10+80+90+320+350+360
=1210
题目要注意几个地方
1、n=0或者1, 答案是0,而不是no answer
2、数据范围大,要用long long
3、n=0或者1的时候也要将数据读入,不能直接输出0就不管输入了,否则会re
4、注意是无向图,建邻接表的时候要双向
5、数据量大,邻接矩阵不行,必须邻接表
搞清楚了这些,剩下就是简单的spfa了
代码:
#include<stdio.h> #define maxN 50005 #define inf 10000000000000 struct EDGE { int v,w; int next; }edge[2 * maxN];//邻接表 int preEdge[2 * maxN];//preEdge[u],上一条以u为起点的边在edge中的位置 long long dis[maxN];//最短路 bool vis[maxN]; int weight[maxN];//节点重量 int queue[20 * maxN];//松弛队列 int n,m; void Init()//初始化 { for (int i = 1; i <= n; ++ i) { preEdge[i] = 0; dis[i] = inf; vis[i] = false; } } void spfa() { int head = 0, tail = 1; queue[0] = 1; dis[1] = 0; while (head < tail) { int u = queue[head]; vis[u] = true; int p = preEdge[u];//以u为起始点 邻接表中第几条边 while (p != 0) { int v = edge[p].v, w = edge[p].w; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) { vis[v] = true; queue[tail] = v; tail ++; } } p = edge[p].next;//下一条以u起点的边 } head ++; vis[u] = false; } bool flag = true; long long sum = 0; for (int i = 2; i <= n; ++ i) { if (dis[i] == inf) { flag = false; break; } sum += weight[i] * dis[i]; } if (!flag) { printf("No Answer\n"); } else printf("%lld\n", sum); } int main() { int t; scanf("%d", &t); while (t --) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++ i) { scanf("%d", &weight[i]); } Init(); int index = 1; for (int i = 1; i <= m; ++ i) { int a, b, c; scanf("%d%d%d", &a, &b, &c); edge[index].v = b; edge[index].w = c; edge[index].next = preEdge[a]; preEdge[a] = index ++; edge[index].v = a; edge[index].w = c; edge[index].next = preEdge[b]; preEdge[b] = index ++; } if (n == 0 || n == 1) { printf("0\n"); continue; } spfa(); } return 0; }