POJ3013 - Big Christmas Tree - 最短路变形

1.题目描述:

Big Christmas Tree
Time Limit: 3000MS Memory Limit: 131072K
Total Submissions: 24123 Accepted: 5236

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

Source

POJ Monthly--2006.09.29, Kim, Chan Min (kcm1700@POJ)
2.题意概述:

给n个点从1到n标号,下面一行是每个点的权,另外给出m条边,下面是每条边的信息,两个端点+权值,边是无向边。你的任务是选出一些边,使这个图变成一棵树。这棵树的花费是这样算的,1号固定为树根,树中每个双亲节点下面的边都有个单价(即边权),然后单价乘上这条边的下面所有的子孙后代的点权和(看sample2,只要除掉边 1 5 9 按照这个方法就能算出1210)

3.解题思路:

把sample2用式子列一下就能发现,每个点的权都要乘上好几条边的权,是哪几条边呢,就是这个点回到点1的路径上的那些边

所以最后的树的花费可以写成  res = sum{  (点权) * (该点回到点1的路径的边权和)  } ,这些点是2到n,1是不用算的

所以决定这条式子大小的,只有  (该点回到点1的路径的边权和)  ,  只要能让它最小即可。  令到每个点回到点1的路径边权和最小是什么?就是最短路啊!

所以从点1运行一次最短路,就可以知道1到每个点的最短路(也就是每个点到点1的最短路,因为无向图)

 

但是要注意一个坑,没看清楚题意,超时了很多次,就是点数可能为0(太坑了),点数为0运行不了最短路一直卡在里面,所以特判一下,输出0,如果点数1同样也是0的,因为整个树不会有边

另外注意网上的各种说法

1.有人说dij过不了,是能过的

2.有人说dij要手写heap才能过,直接用STL的优先队列也是能过的

3.spfa能过

4.这题没试过用vector建图,建议不要,我亲自尝试T了4发


4.AC代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <functional>
#include <cmath>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <map>
#include <set>
#include <ctime>
#define INF 1LL << 60
#define maxn 50010
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
struct node
{
	int to, val, next;
} mp[2 * maxn];
int w[maxn], head[maxn];
ll dis[maxn];
bool vis[maxn];
int cnt;
void init()
{
	memset(head, -1, sizeof(head));
	cnt = 0;
}
void addedge(int u, int v, int w)
{
	mp[cnt].to = v;
	mp[cnt].val = w;
	mp[cnt].next = head[u];
	head[u] = cnt++;
}
void spfa(int sta, int n)
{
	memset(vis, 0, sizeof(vis));
	fill(dis, dis + n + 1, INF);
	deque<int> q;
	vis[sta] = 1;
	dis[sta] = 0;
	q.push_back(sta);
	while (!q.empty())
	{
		int u = q.front();
		q.pop_front();
		vis[u] = 0;
		for (int i = head[u]; i != -1; i = mp[i].next)
		{
			int v = mp[i].to;
			int w = mp[i].val;
			if (dis[v] > dis[u] + w)
			{
				dis[v] = dis[u] + w;
				if (!vis[v])
				{
					vis[v] = 1;
					if (!q.empty() && dis[v] <= dis[q.front()])
						q.push_front(v);
					else
						q.push_back(v);
				}
			}
		}
	}
}
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	long _begin_time = clock();
#endif
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, m;
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
			scanf("%d", &w[i]);
		init();
		while (m--)
		{
			int a, b, c;
			scanf("%d%d%d", &a, &b, &c);
			addedge(a, b, c);
			addedge(b, a, c);
		}
		if (n == 0 || n == 1)
		{
			puts("0");
			continue;
		}
		if (m == 0)
		{
			puts("No Answer");
			continue;
		}
		spfa(1, n);
		ll ans = 0;
		for (int i = 1; i <= n; i++)
			if (dis[i] == INF)
			{
				ans = -1;
				break;
			}
			else
				ans += dis[i] * w[i];
		if (ans == -1)
			puts("No Answer");
		else
			printf("%lld\n", ans);
	}
#ifndef ONLINE_JUDGE
	long _end_time = clock();
	printf("time = %ld ms.", _end_time - _begin_time);
#endif
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值