李耀于NOIP2010集训出的题 Dvalue
此题模型比较明显,求无向图的一棵生成树,使得最大边减去最小边的值最小,这是最小生成树的一个变式
设计出此题的算法需要利用Kruskal贪心的性质,首先枚举一条最小边,接着求原图的一棵最小生成树,根据kruskal的定义,该生成树的最大边必定是在当前枚举的最小边时是所有生成树中最小的。(可能一看题目描述有人就会想二分答案了,我二分只有25分,不过最多也就80分)
那么算法流程就出来了,枚举一条最小边,求最小生成树,用最大边减去最小边更新ans,时间复杂度为O(mlogm+m^2*alpha(n)),但对于此题的数据,这种时间复杂度还是比较勉强的,所以我们可以加上下面几个优化。
枚举一条边时,不考虑比它小的边,那么开始时排序就可以了。
对于每次枚举如果已经构造了N-1条边,直接退掉更新ans,后面的边不用枚举。
多条边权值相同时,只需枚举一条即可。
当前边值-当前枚举的最小边值>=当前ans,那么直接退掉此次枚举。
提供HJW大佬的博客%%%%%orz,链接:http://www.cnblogs.com/huangdalaofighting/
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 struct E 7 { 8 int u,v,d; 9 }e[10001]; 10 struct Node 11 { 12 int next,to; 13 }edge[10001]; 14 bool vis[5001]; 15 int head[5001],num,n,m,ans=2e9,set[5001]; 16 bool cmp(E a,E b) 17 { 18 return a.d<b.d; 19 } 20 int find(int x) 21 { 22 if (set[x]!=x) set[x]=find(set[x]); 23 return set[x]; 24 } 25 int main() 26 {int minx,maxx=0,i,j,u,v,d,l,r,k; 27 //freopen("dvalue.in","r",stdin); 28 //freopen("dvalue.out","w",stdout); 29 cin>>n>>m; 30 minx=2e9; 31 for (i=1;i<=m;i++) 32 { 33 scanf("%d%d%d",&u,&v,&d); 34 maxx=max(maxx,d); 35 minx=min(minx,d); 36 e[i].u=u;e[i].v=v;e[i].d=d; 37 } 38 sort(e+1,e+m+1,cmp); 39 for (i=1;i<=m-n+1;i++) 40 { 41 for (j=1;j<=m;j++) 42 set[j]=j; 43 j=1;k=i; 44 while (j<=n-1&&k<=m) 45 { 46 int p=find(e[k].u),q=find(e[k].v); 47 if (p!=q) 48 { 49 set[p]=q; 50 j++; 51 } 52 k++; 53 } 54 if (j>=n) ans=min(ans,e[k-1].d-e[i].d); 55 } 56 cout<<ans; 57 }