POJ 3013:Big Christmas Tree


Big Christmas Tree
Time Limit: 3000MS Memory Limit: 131072K
Total Submissions: 24201 Accepted: 5249

Description

Christmas is coming to KCM city. Suby the loyal civilian in KCM city is preparing a big neat Christmas tree. The simple structure of the tree is shown in right picture.

The tree can be represented as a collection of numbered nodes and some edges. The nodes are numbered 1 through n. The root is always numbered 1. Every node in the tree has its weight. The weights can be different from each other. Also the shape of every available edge between two nodes is different, so the unit price of each edge is different. Because of a technical difficulty, price of an edge will be (sum of weights of all descendant nodes) × (unit price of the edge).

Suby wants to minimize the cost of whole tree among all possible choices. Also he wants to use all nodes because he wants a large tree. So he decided to ask you for helping solve this task by find the minimum cost.

Input

The input consists of T test cases. The number of test cases T is given in the first line of the input file. Each test case consists of several lines. Two numbers ve (0 ≤ ve ≤ 50000) are given in the first line of each test case. On the next line, v positive integers wi indicating the weights of v nodes are given in one line. On the following e lines, each line contain three positive integers abc indicating the edge which is able to connect two nodes a and b, and unit price c.

All numbers in input are less than 216.

Output

For each test case, output an integer indicating the minimum possible cost for the tree in one line. If there is no way to build a Christmas tree, print “No Answer” in one line.

Sample Input

2
2 1
1 1
1 2 15
7 7
200 10 20 30 40 50 60
1 2 1
2 3 3
2 4 2
3 5 4
3 7 2
3 6 3
1 5 9

Sample Output

15
1210

题目翻译:

给出V个节点E条边。然后给出V个节点的权值,给出某些节点之间边的权值,现在要用这N个节点构成圣诞树(节点1永远作为根),需要边来连接圣诞树中的节点。

圣诞树的边 u-v 的价值等于边 u-v 的权值*(v所在子树中的所有节点的权重和)。求构成圣诞树的最小花费,如果够不成N个节点的数,输出No Answer.

以题目中的图为例。

1-2这条边的价格= weight12 * (nodeweight2+nodeweight3+nodeweight4+nodeweight5+nodeweight6+nodeweight7)

其实对于一个节点,它在走到根节点所经过的每一条路,都需要乘上这个节点的权值。因为这个节点是它所路过节点的子节点,

所以所有路过的边都会成上这个节点的权值。因为一个节点的权值是定值,我们当然希望它距离1的距离越近越好。因此这个题

就是要求所有点到源点1的最短路径在乘上权值的和即是答案。最开始真的没有看出来这是个最短路的题目。需要分析一下才能

知道,它到底要考察什么知识。

注意两个特殊数据:V = 0 和 1 的情况,V = 1时千万不要去输出节点1的权值。要读明白题,构成圣诞树的花费是指

圣诞树中边的最小花费,不要把节点的权值也看成花费了。所以V=1的时候,只有1一个节点,不需要任何边去构成

圣诞树,所以最小花费就是0。还有就是INF要大一些,否则hold不住后台的数据。最短路的值有点大。

最后一点普通Dijkstra会超时,可以用Dijkstra+heap或Shorest Path Fast Algorithm(SPFA)去写。


#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#define INF 0x3f3f3f3f3f

using namespace std;

const int maxn = 50004;
int V,E;            /// V为节点个数,E为边的条数。
int weight[maxn];   /// 存放每个节点的权值。
int head[maxn];
struct Edge
{
    int v;
    long long int w;
    int next;
}edge[maxn<<2];
void SPFA(int start)
{
    queue<int>qu;
    int vis[maxn];
    long long int dis[maxn];
    for(int i = 1; i <= V; i++)
    {
        dis[i] = INF;
        vis[i] = 0;
    }
    int now,nex;
    vis[1] = 1;
    dis[1] = 0;
    qu.push(1);
    while(!qu.empty())
    {
        now = qu.front();
        qu.pop();
        vis[now] = 0;
        for(int i = head[now]; i != -1; i = edge[i].next)
        {
            nex = edge[i].v;
            if(dis[now]+edge[i].w < dis[nex])
            {
                dis[nex] = dis[now]+edge[i].w;
                if(vis[nex]==0)
                {
                    vis[nex] = 1;
                    qu.push(nex);
                }
            }
        }
    }
    long long ans = 0;
    for(int i = 1; i <= V; i++)
    {
        if(dis[i]<INF)
            ans += weight[i]*dis[i];
        else
        {
            printf("No Answer\n");
            return;
        }
    }
    printf("%lld\n",ans);
}
int main()
{
    int T;
    int a,b,c;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&V,&E); ///节点个数,E是边的个数
        for(int i = 1; i <= V; i++)
        {
            scanf("%d",&weight[i]);
            head[i] = -1;
        }
        int j = 0;
        for(int i = 1; i <= E; i++)
        {
            scanf("%d%d%lld",&a,&b,&c);
            edge[j].v = b;
            edge[j].w = c;
            edge[j].next = head[a];
            head[a] = j;
            j++;
            edge[j].v = a;
            edge[j].w = c;
            edge[j].next = head[b];
            head[b] = j;
            j++;
        }
        if(V == 0 || V == 1) printf("0\n");
        else SPFA(1);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值