这道关于最小生成树的问题,起初让我百思不得解,所以就搁置了下来,今天才想着做做,一会儿我就跟你们说说我那可笑的理解、可笑的疑惑!
题目:
问题描述
Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路。道路被用来连接N个牧场,牧场被连续地编号为1到N。每一个牧场都是一个奶牛的家。FJ计划除去P条道路中尽可能多的道路,但是还要保持牧场之间 的连通性。你首先要决定那些道路是需要保留的N-1条道路。第j条双向道路连接了牧场Sj和Ej(1 <= Sj <= N; 1 <= Ej <= N; Sj != Ej),而且走完它需要Lj的时间。没有两个牧场是被一条以上的道路所连接。奶牛们非常伤心,因为她们的交通系统被削减了。你需要到每一个奶牛的住处去安慰她们。每次你到达第i个牧场的时候(即使你已经到过),你必须花去Ci的时间和奶牛交谈。你每个晚上都会在同一个牧场(这是供你选择的)过夜,直到奶牛们都从悲伤中缓过神来。在早上 起来和晚上回去睡觉的时候,你都需要和在你睡觉的牧场的奶牛交谈一次。这样你才能完成你的 交谈任务。假设Farmer John采纳了你的建议,请计算出使所有奶牛都被安慰的最少时间。
输入格式
第1行包含两个整数N和P。
接下来N行,每行包含一个整数Ci。
接下来P行,每行包含三个整数Sj, Ej和Lj。
输出格式
输出一个整数, 所需要的总时间(包含和在你所在的牧场的奶牛的两次谈话时间)。
样例输入
5 7
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
样例输出
176
数据规模与约定
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
当我看到这个问题时,我首先被一句话搞懵了,“你每个晚上都会在同一个牧场(这是供你选择的)过夜”,这句话是在告诉我们什么?他要在这里住好几天?还是......最后我终于明白,考虑到最小生成树的问题的实现,这里肯定是在一天内完成交谈任务,并且回到初始的位置。那么我们就可以跟据题意知道,每条选择的路我们都要考虑到去与回,并且要考虑到谈话时间。然后我自己画了一个小的树,实际的去模拟了一下操作的过程,发现,除了起始位置外,其他各个结点(牧场)与牛的谈话次数均和边数相等,只有起始位置的多了一次,那么我们就好理解了,用起始位置多的这一次谈话时间(为了使时间最短,则需要选取最少的谈话时间的结点做起点)加上最小生成树的时间,即: min + Kruskal()。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#define M 100002
typedef struct Edge
{
int S;
int E;
int L;
} Edge;
Edge e[M];
int far[M];
int C[M];
int sum = 0;
int N, P;
int cmp(const void *a, const void *b)
{
Edge *c = (Edge *)a;
Edge *d = (Edge *)b;
return c->L - d->L;
}
int find(int x)
{
int i, k, r;
r = x;
while (far[r] >= 0)
r = far[r];
k = x;
while (k != r)
{
i = far[k];
far[k] = r;
k = i;
}
return r;
}
void Union(int S, int E)
{
int rS, rE;
int num;
rS = find(S);
rE = find(E);
num = far[rS] + far[rE];
if(far[rS] < far[rE])
{
far[rE] = rS;
far[rS] = num;
}
else
{
far[rS] = rE;
far[rE] = num;
}
}
int Kruskal()
{
int i;
int S, E;
int sumweight = 0, count = 0;
for(i = 0; i < N; i++)
far[i] = -1;
qsort(e, P, sizeof(e[0]), cmp);
for(i = 0; i < P; i++)
{
S = e[i].S;
E = e[i].E;
if(find(S) != find(E))
{
sumweight += e[i].L;
Union(S, E);
count++;
if(count >= N - 1)
{
break;
}
}
}
return sumweight;
}
int main ()
{
int i, min = M;
int S, E, L;
scanf ("%d %d", &N, &P);
for (i = 0; i < N; i++)
{
scanf ("%d", &C[i]);
if (C[i] < min)
min = C[i];
}
for (i = 0; i < P; i++)
{
scanf("%d %d %d",&S, &E, &L);
e[i].S = S - 1;
e[i].E = E - 1;
e[i].L = L * 2 + C[S - 1] + C[E - 1];
}
printf ("%d\n", min + Kruskal());
return 0;
}
具体的原理以及思想都在代码中注释清楚了,希望有利于理解。
OVER!!!