hdu--4081--次小生成树<Kruskal--cool>

我记得 大概是几个月前吧 那时候就看过 次小生成树了 虽然感觉听上去很简单 就是放条边进去 那条边出来 但实现起来不简单那=-=

这次 正好做到这题 是次小生成树的 就又去学了下...---传送

要是懒得点击链接 我把核心思想写下来.

一种容易想到的方法是枚举删除最小生成树上的边,再求最小生成树。用kruskal这种算法的复杂度为O(n*elog2e),当图比较稠密时,复杂度接近O(n^3)。

 但有一种更简单的方法:先求最小生成树T,枚举添加不在T中的边,则添加后一定会形成环。找到环上边值第二大的边(即环中属于T中的最大边),把它删掉,计算当前生成树的权值,取所有枚举修改的生成树的最小值,即为次小生成树。

 这种方法在实现时有更简单的方法:首先求最小生成树T,然后从每个结点u遍历最小生成树T,用一个二维数组max[u][v]记录结点u到结点v的路劲上 边的最大值(即最大边的值)。然后枚举不在T中的边(u,v),计算T- max[u][v] + w(u,v)的最小值,即为次小生成树的权值。显然,这种方法的时间复杂度为O(n^2 + e)。

   见,第二种算法将原来的时间复杂度O(n^3)提高到了O(n^2)。

PS:对于这段话 我再补充一些我的个人想法 方便理解:

max[u][v]数组的值都是属于mst集合的边的权值

 

...你可以看到 这个图的mst的最小值是10--这边我假设我的mst是由A->B B->C C->D这3边构成

你可以看到 max[A][D]是5<在A->D的路径上有B->C==5,这里选取B->C是因为它是属于mst之中的>这里一定要知道我们在拿u结点进行遍历 对于任意max[u][v]出现过的值都是属于mst集合之中的值

然后我们进行操作 可以得到一个ans = mst - max[A][D] + e[A][D]这个 e[A][D]就是指AD这条边的权值 然后我们暂时记secondMst的值为10-5+6==11

然后我们继续操作 因为我们是进行O(m)的遍历 ,m是指图中所有的边

你可以看到 maax[B][D]是5 同样进行计算 mst - max[B][D] + e[B][D] 就是 10-5+5=10 小于刚刚得到的ans=11 所以 更新为10

然后 继续操作 发现最后一条边C->D 是属于mst集合之中的 就直接可以看做continue 然后因为遍历完成 就退出循环了

然后 比较 mst == secondMst 得出 该图有次小生成树

 

然后这题的话 是因为有个magic road的存在 使它可以用次小生成树来解决

前面的步骤处理 和上面差不多 因为代码实在是太长了 我也附加了一点注释

只是 当你处理到遍历所有边 来求ans的时候 注意下 如果这条边是属于mst之中与不属于mst之中的操作是不一样的

该边不属于mst

temp = ( peo[ tree[i].st ] + peo[ tree[i].end ] )*1.0 / ( mst - maxDist[ tree[i].st ][ tree[i].end ] ); 

除式的前面就是2个城市的人口 除式的后面-- 最小生成树的值 - 从st->end这条路径上所有出现的边中的最大值<将它设置为magic road>所以就减去它

该边属于mst

temp = ( peo[ tree[i].st ] + peo[ tree[i].end ] )*1.0 / ( mst - tree[i].dist );

除式前面同上  除式的后面--因为该边属于mst就直接减掉它

************************************************************

次小生成树 蛮不错的 理解了它 对于其它算法的理解与学习 也是有帮助的...就像我好像在前面博客说过的 可能你学习一段时间后 再去看前面的以前模棱两可的东西 会觉得清晰许多

 

  1 #include <iostream>
  2 #include <cstring>
  3 #include <queue>
  4 #include <algorithm>
  5 #include <iomanip>
  6 #include <cmath>
  7 using namespace std;
  8 
  9 int cnt , num , n;
 10 const int size = 1010;
 11 
 12 int x[size];
 13 int y[size];
 14 int peo[size];//记录每个城市的人数
 15 int father[size];
 16 double maxDist[size][size];//记录从i->j这条路径上的边的最大值
 17 struct Tree//将所有2个城市之间的信息存入这里 st与end存入的是在0-n的当中编号 这边相当于将(x,y)这个坐标以一个点来表示
 18 {
 19     int st;
 20     int end;
 21     double dist;
 22     bool flag;
 23 }tree[size*size];
 24 struct graph //最小生成树的结点
 25 {
 26     int to;
 27     int next;
 28     double dist;
 29 }edge[size*2];
 30 int head[size];
 31 struct data//求次小生成树过程中需要的结点信息
 32 {
 33     int id;//当前结点
 34     double maxDist;//从遍历点i到当前结点的最大边的距离
 35     data( int u , double v):id(u),maxDist(v){};
 36     data(){};
 37 };
 38 bool cmp( const Tree p , const Tree q )
 39 {
 40     return p.dist < q.dist;//按边的权值从小到大进行排序--这边就是2点之间的距离大小
 41 }
 42 void init( )
 43 {
 44     memset( father , -1 , sizeof(father) );
 45     memset( head , -1 , sizeof(head) );
 46 }
 47 int find( int x )
 48 {
 49     return father[x] == -1 ? x : father[x] = find( father[x] );
 50 }
 51 void add1( int from , int to , double dist )
 52 {
 53     edge[cnt].to = to;
 54     edge[cnt].dist = dist;
 55     edge[cnt].next = head[from];
 56     head[from] = cnt++;
 57 }
 58 
 59 void add2( int st , int end , double dist )
 60 {
 61     tree[num].st = st;
 62     tree[num].end = end;
 63     tree[num].dist = dist;
 64     tree[num++].flag = false;
 65 }
 66 
 67 double getDist( int p , int q )
 68 {
 69     return sqrt( ( (x[p]-x[q])*(x[p]-x[q]) + (y[p]-y[q])*(y[p]-y[q])*1.0 ) );
 70 }
 71 
 72 double kruskal( )
 73 {
 74     init();
 75     int x , y;
 76     int edgeNum = 0;
 77     double ans = 0;
 78     sort( tree , tree+num , cmp );
 79     for( int i = 0 ; i<num ; i++ )//这里的i都是指第 I 条边 
 80     {
 81         x = find( tree[i].st );
 82         y = find( tree[i].end );
 83         if(x!=y)//如果这条边的两个端点不属于一个连通块 就可以进行合并
 84         {
 85             edgeNum ++;
 86             add1( tree[i].st,tree[i].end,tree[i].dist );
 87             add1( tree[i].end,tree[i].st,tree[i].dist );
 88             tree[i].flag = true;//加入mst之中
 89             father[x] = y;
 90             ans += tree[i].dist;
 91         }
 92         if( edgeNum == n-1 )
 93             break;
 94     }
 95     return edgeNum == n-1 ? ans : -1;
 96 }
 97 
 98 void bfs( int p )
 99 {
100     data now;
101     bool vis[size];
102     memset( vis , false , sizeof(vis) );
103     queue<data>q;
104     q.push( data(p,0) );
105     vis[p] = true;
106     while( !q.empty() )
107     {
108         now = q.front();
109         q.pop();
110         for( int i = head[now.id] ; i!=-1 ; i = edge[i].next )
111         {
112             int end = edge[i].to;
113             double dist = edge[i].dist;
114             if( !vis[end] )
115             {
116                 if( now.maxDist>dist )
117                     dist = now.maxDist;
118                 maxDist[p][end] = dist;
119                 vis[end] = true;
120                 q.push( data(end,dist) );
121             }
122         }
123     }
124 }
125 
126 void secondMst( double mst )
127 {
128     double ans = -1;
129     for( int i = 0 ; i<n ; i++ )
130     {
131         bfs( i );
132     }    
133     for( int i = 0 ; i<num ; i++ )//num遍历所有的边-尝试将该边设置为magic road--根据该边是否属于mst集合 进行不同操作
134     {
135         double temp;
136         if( !tree[i].flag )//该边不属于mst集合之中
137         {
138             temp = ( peo[ tree[i].st ] + peo[ tree[i].end ] )*1.0 / ( mst - maxDist[ tree[i].st ][ tree[i].end ] );  
139         }
140         else
141         {
142             temp = ( peo[ tree[i].st ] + peo[ tree[i].end ] )*1.0 / ( mst - tree[i].dist );
143         }
144         ans = max( ans , temp );
145     }
146     cout<<setiosflags(ios::fixed);
147     cout<<setprecision(2)<<ans<<endl;
148 }
149 
150 int main()
151 {
152     cin.sync_with_stdio(false);
153     int t;
154     cin >> t;
155     while( t-- )
156     {
157         num = cnt = 0;
158         cin >> n;
159         for( int i = 0 ; i<n ; i++ )
160         {
161             cin >> x[i] >> y[i] >> peo[i];
162         }
163         for( int i = 0 ; i<n ; i++ )
164         {
165             for( int j = 0 ; j<i ; j++ )
166             {
167                 double val = getDist(i,j);
168                 add2(i,j,val);
169             }
170         }
171         double mst = kruskal();
172         secondMst( mst );
173     }
174     return 0;
175 }
View Code

这里有个很让人抓狂的地方...用C++超时 G++AC 卧槽了.............

 

today:

  有jr发帖

  给在最好年纪陪伴你却最终没走到一起的女孩子留下一句话:<摘取一部分>

  多年后,你离了,我未娶,你和孩子我都要

  感谢那时你,温暖我横冲直撞的年纪。

  以前没钱,穷小子一个,你陪我吃了好久的路边摊。前几天和另一个女孩去吃,她说:多脏啊,女生不吃这个“的时候,才知道那些年多么珍贵

  从此我爱上的人,都像你。。。。。。。
  不要亮,也逛bxj

  

  求着要做你的备胎,不是非要在你身边。
  是我怕你找不到那样一个你想要的能在精神和物质上都给你满足的他时,
  我能,给你煮泡面,为你洗衣服,攒很久的钱为你买瓶DHC,为你淘双新百伦。。


  你说的,每个女孩都希望有个好老公,你不过很普通而已。
  你说,你太自私,但是你爱我。
  你说,我不懂圆滑,爱得罪人,出力不讨好,在社会上吃不开。
  你说,你想在北京,有房有车,生活殷实,工作清闲。
  你说你会爱你的家人。


  谢谢你在最纯真年代的陪伴;
  谢谢我在最好的年华的抛弃;
  谢谢你再三犹豫后无法和我面对最难的人生。

  还是希望你能找到你的生活。希望你一直过的比我好。

 

  你不想来我家乡,我就过去呗;你不喜欢我不会喝酒,我就逼自己去喝;你不喜欢我哪里,我都改,好不好.....我只是很想很想和你在一起。工作了,分手 了,终于有一晚喝多了,忍不住给你打了电话,哭喊了一晚上的名字,最后你还是不言不语。你该了解我的,我有多倔强,甚至连家里人都不允许说你一句坏话,甚 至妈妈担心的对我说出以后你们离这么远,分手怎么办,我都会很生气说不要说....我只是想起你的时候还是很痛,爱到最后,只有放不开的犯贱,从来都没有 因为感动而回来的爱人。 说来很搞笑,前两天陪暧昧对象去看《闺蜜》,看到希汶最后找林杰说的那段话:你不想这么早结婚,我们就不要这么早结婚嘛;你不想去巴黎度蜜月,我们就不去 巴黎度蜜月啊;你想去哪里我都陪你去。好不好。我都改嘛,好不好.....看到这一段,想到自己,竟然不能自已,一个大老爷们在电影院为一部女生电影泣不 成声。 顺便说一句,在你之后,再也不能像以前对你一样,那么心甘命抵的去爱一个人,再也回不去了。祝君安好~

 

  “你永远是我爱得最纯粹最深刻的人。”

 

posted @ 2014-08-20 20:54  radical  阅读(1838)  评论(0编辑  收藏  举报