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 }