这道题目咋一看就是最小生成树啊。
题目描述大概是这样:
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;
}