速度优化——POJ3697

本文解析了一道ACM竞赛中的图论题目,重点介绍了使用哈希表优化广度优先搜索(BFS)算法的过程及技巧。文章分享了从初始的链表存储到哈希表实现的优化路径,包括如何有效避免重复搜索已访问节点,以及通过选择素数等手段提高哈希效率。

 

这道题是ACM合肥赛的最后一题,题目大概题意如下:

给定一个包含N1 ≤ N ≤ 10,000)个顶点的无向完全图,图中的顶点从1N依次标号。从这个图中去掉M0 ≤ M ≤ 1,000,000)条边,求最后与顶点1联通的顶点的数目。(时限4s

基本算法肯定是BFS或者DFSBFS思想为从顶点1开始不断扩展,广度优先搜索所有的与当前扩展点联通的顶点。开始每次都要判断所有的顶点,因此BFSON^2)的复杂度。

每一次扩展都需要判断该边是否在断开的边的集合中。判断需要的时间取决于M条断开边集的存储方式。初始我是用一个链表存储,索引为该边依附的顶点,然后直接搜索,复杂度为ON^2×N=ON^3),超时。然后我又将每个顶点对应边集进行排序,并且二分搜索,这样也很慢,复杂度为ON^2×logN)。另外一种思路是改变断开边集的存储方式,直接用10000×10000bool型数组存储顶点i是否和j联通,这样虽然时间上达到了ON^2)的复杂度,但是把内存爆掉了。

最终Sempr同学提供了他的解决方法:hash。其实我刚开始也想到了hash,但是想不出如何进行hash存储,看来还是hash用的太少。具体的方法是,通过数组模拟链表,由另外一个数组记录当前hash code对应的节点在数组中的位置。这样的hash让复杂度基本降低到了ON^2)。

在进行BFS的时候为了防止对那些已经扩展的节点重复扩展,通过一个数组保存那些尚未扩展到节点,只对这些点进行扩展。

最后是一些提示:

1、  hash 的时候,选择素数进行相关操作会比和数快很多;

2、  Vectormemory pool的方式,使得它的内存占用比实际申请的多的多,因此最好用数组

3、  少用pair,可以用struct pair{int first,second;}来进行模拟。

以下是代码:

  1. struct node 
  2. {
  3.     int first,second;
  4. };
  5. const int mod = 1000117;
  6. const int smod = 10091;
  7. node edges[mod];
  8. int next[mod],hash[mod],endList,q[10010],lst[10010],m,n;
  9. void insertNode(int a,int b)    //  a<b
  10. {
  11.     int code = (a*smod+b)%mod;
  12.     edges[endList].first=a;
  13.     edges[endList].second=b;
  14.     next[endList]=hash[code];
  15.     hash[code]=endList++;
  16. }
  17. bool search(int a,int b)    //  a<b
  18. {
  19.     if(a>b) swap(a,b);
  20.     int code = (a*smod+b)%mod;
  21.     for (int i=hash[code];i>-1;i=next[i])
  22.         if(edges[i].first==a && edges[i].second==b) return true;
  23.     return false;
  24. }
  25. int main()
  26. {
  27.     int cnt=0,a,b,head,tail,lstSize,cur,temp;
  28.     while (scanf("%d%d",&m,&n))
  29.     {
  30.         if(!m && !n) break;
  31.         endList=0; ++cnt;
  32.         memset(hash,-1,sizeof hash);
  33.         while (n--)
  34.         {
  35.             scanf("%d%d",&a,&b);
  36.             if(a<b) insertNode(a,b);
  37.             else insertNode(b,a);
  38.         }
  39.         lstSize=0;
  40.         for (int i=2;i<=m;++i) lst[lstSize++]=i;
  41.         q[0]=1; head=0; tail=1;
  42.         while (head<tail)
  43.         {   
  44.             cur = q[head++];
  45.             temp = 0;
  46.             for (int i=0;i<lstSize;++i)
  47.             {
  48.                 if (!search(cur,lst[i])) q[tail++]=lst[i];
  49.                 else lst[temp++]=lst[i];
  50.             }
  51.             lstSize=temp;
  52.         }
  53.         printf("Case %d: %d/n",cnt,tail-1);
  54.     }
  55.     return 0;
  56. }

 

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值