您的位置:首页 > 其它

poj 3013 Big Christmas Tree(最短路变形)

2016-09-07 21:20 435 查看
传送门:POJ 3013 Big Christmas Tree

描述:

Big Christmas Tree

Time Limit: 3000MS Memory Limit: 131072K
Total Submissions: 23338 Accepted: 5042
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 v,e (0 ≤ v, e ≤ 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 a, b, c 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)

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

 

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

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

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

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

 

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

还要注意用long long,写代码的太急了,直接套模板就没改数据类型,WA了好几发_(:зゝ∠)_

另外注意网上的各种说法

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

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

3.spfa能过

4.用vector可以建图,建议不要,确实效率不太高,毕竟点很多。下面就给出两个不同建图方式的代码

代码一:
(vector建图)

//G++ 2516ms  4760k
#include <iostream>
#include <cstdio>
#include <cstring>
#include<vector>
#include<queue>
#define ll __int64
using  namespace std;

/*vector建图
点很多的话效率会比较低
*/
const ll inf= 0x3f3f3f3f3f3f3f3fLL;
const  int  maxn=50010;
vector<pair<int,int> >E[maxn];//邻接表
ll d[maxn];
bool done[maxn];
int val[maxn];
int v,e;

void  init(){
for(int i=0; i<maxn; i++)E[i].clear();
for(int i=0; i<maxn; i++)done[i]=false;
for(int i=0; i<maxn; i++)d[i]=inf;
}

ll dijkstra(int s,int t){
priority_queue<pair<ll,int> >q;
int cnt=0;
ll res=0;
d[s]=0;
q.push(make_pair(-d[s],s));//优队返回的是最大值,取负就返回的最小值
while(!q.empty()){
int now=q.top().second;
q.pop();
if(done[now]) continue;
done[now]=true;
cnt++;
res+=d[now]*val[now];
for(int i=0; i<E[now].size(); i++){
int  v=E[now][i].first;
if(d[v]>d[now]+E[now][i].second){
d[v]=d[now]+E[now][i].second;
q.push(make_pair(-d[v],v));
}
}
}
if(cnt<v) return -1;
else return res;
//熟悉heap+dij的本质,可以在dij过程中就统计出所有点的最短路和并乘上他们的权
//不需要dij结束后去扫描一次所有点来计算,并且可以在dij过程判断是否能构成一棵树
}

int  main(){
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d%d",&v,&e);
for(int i=1; i<=v; i++)scanf("%d",&val[i]);
for(int i=1; i<=e; i++){
int x,y,z;scanf("%d %d %d",&x,&y,&z);
E[x].push_back(make_pair(y,z));  //边x,y,边权z
E[y].push_back(make_pair(x,z));
}
if(v == 0 || v == 1){ //注意这个坑,会超时,出现n=0点的时候
printf("0\n");
continue;
}
ll ans=dijkstra(1, v);
if(ans != -1)printf("%I64d\n",ans);
else puts("No Answer");
}
return  0;
}


代码二:

显然这种不用vector建图效率更高

//G++ 594ms 3076k
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
#define ll __int64
using namespace std;

/*结构体建图
*tol记得初始化
*/
const int N = 50010;
const ll inf= 0x3f3f3f3f3f3f3f3fLL;
int n,m,tol;
ll d
;
int head
;
bool done
;
int val
;
struct edge{
int u,v,w,next;
}e[2*N];
typedef pair<ll,int>P;
priority_queue<P, vector<P>, greater<P> > q;//取最小值

void add(int u,int v,int w){
e[tol].u=u; e[tol].v=v; e[tol].w=w;
e[tol].next=head[u]; head[u]=tol++;
u=u^v; v=u^v; u=u^v;//异或
e[tol].u=u; e[tol].v=v; e[tol].w=w;
e[tol].next=head[u]; head[u]=tol++;
}

ll Dij(){
int cnt=0;
ll res=0;
d[1]=0;
q.push(make_pair(d[1], 1));
while(!q.empty()){
int u,v,w;
P x=q.top();
q.pop();
u=x.second;
if(done[u]) continue;
done[u]=true;
cnt++;
res+=d[u]*val[u];
for(int i=head[u]; i!=-1; i=e[i].next){
v=e[i].v;
w=e[i].w;
if(d[u]+w<d[v]){
d[v]=d[u]+w;
q.push(make_pair(d[v], v));
}
}
}
if(cnt<n) return -1;
else return res;
}

int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&val[i]);
while(!q.empty()) q.pop();
tol=0;
for(int i=1; i<=n; i++){
head[i]=-1;
d[i]=inf;
done[i]=false;
}
for(int i=1; i<=m; i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
if(n == 0 || n == 1){ //注意这个坑,会超时,出现n=0点的时候
printf("0\n");
continue;
}
ll ans=Dij();
if(ans != -1) printf("%I64d\n",ans);
else printf("No Answer\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: