usaco 安慰奶牛

Description

约翰有N个牧场,编号依次为1到N。每个牧场里住着一头奶牛。连接这些牧场的有P条 道路,每条道路都是双向的。第j条道路连接的是牧场Sj和Ej,通行需要Lj的时间。两牧场之 间最多只有一条道路。约翰打算在保持各牧场连通的情况下去掉尽量多的道路。

约翰知道,在道路被强拆后,奶牛会非常伤心,所以他计划拆除道路之后就去忽悠她们。 约翰可以选择从任意一个牧场出发开始他维稳工作。当他走访完所有的奶牛之后,还要回到 他的出发地。每次路过牧场i的时候,他必须花Ci的时间和奶牛交谈,即使之前已经做过工 作了,也要留下来再谈一次。 注意约翰在出发和回去的时候,都要和出发地的奶牛谈一次话。 请你计算一下,约翰要拆除哪些道路,才能让忽悠奶牛的时间变得最少?

Input Format

第一行:两个用空格分开的整数: N和P,5 ≤ N ≤ 10,000,N − 1 ≤ P ≤ 100,000

第二行到N + 1行:第i + 1行包括一个整数Ci,1 ≤ Ci ≤ 1,000

第N + 2行到N + P + 1行:第N + j + 1行包括三个用空格分开的整数Sj,Ej和Lj, 1 ≤ Sj, Ej ≤ N, Sj ≠ Ej,0 ≤ Lj ≤ 1,000

Output Format

第一行:一个整数,表示忽悠所有奶牛需要的最少时间

----------------------------------------------------------------

正解=最小生成树

显然拆完路后,这是一棵树,

题目要求访问所有点,

不难发现,除根节点外,每一个点的访问次数=该点的度

每给当前生成树一条边,则该边连接的两点都会增加一个度

因此一条边的实际权值 = 边长+该边所连两点的点权

然后用Kruskal做最小生成树即可

根结点的反问次数会多一,所以直接找个点权最小的点做根节点即可

代码如下:

 1 #include<cstring>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<string>
 5 #include<iostream>
 6 #define INF 999999
 7 using namespace std;
 8 struct Edge{
 9     int x,y,v;
10 }a[1000001];
11 int Min=INF,ans,N,P,f[10001],c[10001];
12 int find(int u){
13     return f[u]==u ? u : f[u]=find(f[u]);
14 }
15 bool cmp(const Edge&X,const Edge&Y){
16     return X.v<Y.v;
17 }
18 void kruskal(){
19     for(int i=1;i<=N;i++) f[i]=i;
20     sort(a+1,a+1+P,cmp);
21     for(int i=1;i<=P;i++) if(find(a[i].x)!=find(a[i].y)){
22       ans+=a[i].v;
23       f[find(a[i].x)]=f[find(a[i].y)];
24     }
25 }
26 int main(){
27     scanf("%d%d",&N,&P);
28     for(int i=1;i<=N;i++){
29       scanf("%d",&c[i]);
30       Min=min(c[i],Min);
31     }
32     for(int i=1;i<=P;i++){
33       scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
34       a[i].v+=a[i].v+c[a[i].x]+c[a[i].y];
35     }
36     kruskal();
37     printf("%d",ans+Min);
38 }
View Code

posted @ 2013-10-22 09:48  Mr. Black  阅读(863)  评论(0编辑  收藏  举报