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

你可能感兴趣的:(USACO)