【poj3522-苗条树】最大边与最小边差值最小的生成树,并查集

【poj3522-苗条树】最大边与最小边差值最小的生成树,并查集

题意:求最大边与最小边差值最小的生成树。n<=100,m<=n*(n-1)/2,没有重边和自环。

题解:

m^2的做法就不说了。

时间复杂度O(n*m)的做法:

按边排序,枚举当前最大的边。

那也就是说,把边排序之后从小到大编号,要在[1,r]这段区间内生成一棵最大边与最小边差值最小的生成树。

那每次生成肯定不行(这就是暴力m^2做法。。),我们考虑继承。

假设[1,r-1]这段区间内的苗条树已经生成,那我们只需要把当前第r条边加进去。

加进去分两种情况:

x和y还没有联通:直接加边

x和y已经联通:这样树上就形成了一个环,我们把环上最小的边删除,那就是当前的苗条树。

 

我维护一个fa[x]表示x节点的fa是谁,dis[x]表示x节点到fa节点的距离。

找环:

先不把那条边加进去,在x,y暴力不断fa上跳找出lca。x->lca->y->x就是那个环。

一开始lca那里错了。。

暴力找lca:

 1 int find_lca(int x,int y)
 2 {
 3     memset(inx,0,sizeof(inx));
 4     memset(iny,0,sizeof(iny));
 5     int t=0,k=0;
 6     while(x) t++,inx[x]=t,x=fa[x];
 7     while(y) k++,iny[y]=k,y=fa[y];
 8     int z=0;
 9     for(int i=1;i<=n;i++)
10     {
11         if(inx[i] && iny[i])
12         {
13             if(z==0) z=i;
14             else if(inx[i]<inx[z] || iny[i]<iny[z]) z=i;
15         }
16     }
17     return z;
18 }

 

删边:

这道题涉及删边丫。。又要n*m做出来。。

fa[]其实就是一条有向边,其实我们只需要修改fa[]和dis[]就可以了。

我就t0表示那个fa[id]所代表的有向边是要删除的 是在x还是y。

具体看代码把。。注意暴栈(我也不知道为什么),就改成了数组+while。

 

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int N=400,M=2*200010,INF=(int)1e9;
  9 struct node{
 10     int x,y,d,next,bk;
 11 }a[M];
 12 int n,m,t0,t1,id,f[N],fa[N],dis[N],inx[N],iny[N],c[N],p[N];
 13 
 14 bool cmp(node x,node y){return x.d<y.d;}
 15 int maxx(int x,int y){return x>y ? x:y;}
 16 int minn(int x,int y){return x<y ? x:y;}
 17 
 18 int findfa(int x)
 19 {
 20     if(f[x]==x) return f[x];
 21     return findfa(f[x]);
 22 }
 23 
 24 int find_lca(int x,int y)
 25 {
 26     memset(inx,0,sizeof(inx));
 27     memset(iny,0,sizeof(iny));
 28     int t=0,k=0;
 29     while(x) t++,inx[x]=t,x=fa[x];
 30     while(y) k++,iny[y]=k,y=fa[y];
 31     int z=0;
 32     for(int i=1;i<=n;i++)
 33     {
 34         if(inx[i] && iny[i])
 35         {
 36             if(z==0) z=i;
 37             else if(inx[i]<inx[z] || iny[i]<iny[z]) z=i;
 38         }
 39     }
 40     return z;
 41 }
 42 
 43 void find_min(int x,int y,int z)
 44 {
 45     int mn=INF,xx=x,yy=y;
 46     while(x!=z) 
 47     {
 48         if(dis[x]<mn) mn=dis[x],id=x,t0=xx,t1=yy;
 49         x=fa[x];
 50     }
 51     while(y!=z)
 52     {
 53         if(dis[y]<mn) mn=dis[y],id=y,t0=yy,t1=xx;
 54         y=fa[y];
 55     }
 56 }
 57 
 58 void change(int x,int pre)
 59 {
 60     int pl=0;
 61     c[++pl]=x;p[pl]=pre;
 62     while(x && x!=id)
 63     {
 64         c[++pl]=fa[x];p[pl]=x;
 65         x=fa[x];
 66     }
 67     for(int i=pl;i>=1;i--)
 68     {
 69         fa[c[i]]=p[i];
 70         dis[c[i]]=dis[p[i]];
 71     }
 72 }
 73 
 74 int main()
 75 {
 76     freopen("a.in","r",stdin);
 77     freopen("a.out","w",stdout);
 78     while(1)
 79     {
 80         scanf("%d",&n);
 81         if(!n) return 0;
 82         scanf("%d",&m);
 83         memset(fa,0,sizeof(fa));
 84         memset(dis,0,sizeof(dis));
 85         for(int i=1;i<=n;i++) f[i]=i;
 86         for(int i=1;i<=m;i++)
 87         {
 88             scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
 89             a[i].x++,a[i].y++;
 90         }
 91         sort(a+1,a+1+m,cmp);
 92         int x,y,z,d,mn,cnt=0,ans=INF;
 93         for(int i=1;i<=m;i++)
 94         {
 95             x=a[i].x,y=a[i].y,d=a[i].d;
 96             if(findfa(x)!=findfa(y)) 
 97             {
 98                 cnt++;
 99                 id=0;change(x,y);
100                 dis[x]=d;
101                 f[findfa(x)]=findfa(y);
102             }
103             else
104             {
105                 z=find_lca(x,y);
106                 find_min(x,y,z);
107                 change(t0,t1);
108                 dis[t0]=d;
109             }
110             if(cnt>=n-1) 
111             {
112                 mn=INF;
113                 for(int j=1;j<=n;j++) 
114                     if(fa[j]) mn=minn(mn,dis[j]);
115                 ans=minn(ans,d-mn);
116             }
117         }
118         printf("%d\n",ans);
119     }
120     return 0;
121 }

 

posted @ 2016-11-08 11:27 拦路雨偏似雪花 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值