试题 算法训练 安慰奶牛
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。
10
10
20
6
30
1 2 5
2 3 5
2 4 12
3 4 17
2 5 15
3 5 6
5 <= N <= 10000,N-1 <= P <= 100000,0 <= Lj <= 1000,1 <= Ci <= 1,000。
题目一边读下来就觉得很复杂,不知道最小生成树的源点应该设在哪,百度了一下才知道,原来自己多画几个图就能明白个中奥妙,不知道怎么做的时候一定要动手动手,写写画画理清思路!一点就通
于是通过观察可以发现:各个结点的访问次数恰好和其邻边数相等,只有源点的访问次数多了一次,那么只要选取点权最小的结点作为源点,就能得到答案,即所需最少时间是 min + Kruskal()。
需要注意的是,因为要回到源点,所以每条边都会经过两次,并且除了边权,结点也有权值,我们需要更改边权为 L*2+C1+C2(即边权*2+这条边的两个端点的权值)。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cstring> 5 #include <string> 6 #include <cmath> 7 #include <algorithm> 8 #define INF 0x3f3f3f3f 9 10 using namespace std; 11 typedef long long ll; 12 const ll mod=1000000007; 13 const int max_n=10005; 14 const int max_p=100005; 15 16 int par[max_n];//根结点 17 int n, p;//牧场数(结点),道路数(边) 18 int c[max_n];//谈话时间 19 20 struct edge { 21 int from, to, cost;//起点,终点,边权 22 }e[max_p]; 23 24 int cmp(edge x, edge y) { 25 return x.cost<y.cost; 26 } 27 //初始化根结点为该结点本身 28 void init() { 29 for(int i=1; i<=n; i++) { 30 par[i]=i; 31 } 32 } 33 //查找根结点 34 int fid(int x) { 35 if(par[x]==x) return x; 36 else return par[x]=fid(par[x]); 37 } 38 //合并两集合 39 void unite(int x, int y) { 40 par[y]=par[x]; 41 } 42 //最小生成树 43 int kruskal() { 44 sort(e, e+p, cmp);//把边按权值从小到大排序 45 int cnt=0;//构建的最小生成树加入的边数 46 int ans=0;//最小生成树的边权总和 47 for(int i=0; i<p; i++) { 48 if(cnt==n-1) break;//已构建好最小生成树 49 int a=fid(e[i].from); 50 int b=fid(e[i].to); 51 if(a!=b) { 52 unite(a, b); 53 ans+=e[i].cost; 54 cnt++; 55 } 56 } 57 return ans; 58 } 59 60 int main() { 61 int minc=1005;//最小谈话时间 62 int u, v, w; 63 cin>>n>>p; 64 for(int i=1; i<=n; i++) { 65 scanf("%d", &c[i]); 66 minc=min(minc, c[i]); 67 } 68 for(int i=0; i<p; i++) { 69 scanf("%d %d %d", &u, &v, &w); 70 e[i].from=u; 71 e[i].to=v; 72 e[i].cost=w*2+c[u]+c[v]; 73 } 74 init(); 75 int ans=minc+kruskal(); 76 printf("%d\n", ans); 77 return 0; 78 }