POJ 2949 SPFA

对一个权值可正可负的图SPFA判是否存在正环


首先引用一篇国家集训队论文《spfa的优化及应用》——姜碧野
给个百度传送门http://wenku.baidu.com/view/9e1231126edb6f1aff001f25.html
orz


POJ 2949(先将该题题意转化到二分答案判正环这一步)
//node为点的个数,
//基础代码5000+ms危险过
bool spfa(double x)
{   int hea,tail,now;
    for (int i=1;i<=node;i++) //*1
       {d[i]=0; 		//为什么可以这样详见论文
 	tr[i]=i; 	 	//tr数组模拟队列
        v[i]=1; 		//v[i]代表i是否在队列中
 	vv[i]=0; 		//vv[i]代表i代表i入队次数,*2
    }
    hea=0; tail=node; 
    while (hea!=tail)
    {   hea=(hea+1)%N;
        now=tr[hea];
        v[now]=0; //*3
        for (int k=head[now];k!=-1;k=e[k].next)
             if (d[e[k].to]<d[now]+e[k].w-x)//x是二分的答案,此题特有的
             {  d[e[k].to]=d[now]+e[k].w-x;
                if (!v[e[k].to])
                     {vv[e[k].to]++;
                      if (vv[e[k].to]==node) return 1;
                      if (e[k].to==now) return 1;  //*4
                      v[e[k].to]=1;
                      tail=(tail+1)%N;
                      tr[tail]=e[k].to;
                     }

             }
 	//v[now]=0不能放这里

    }
    return 0;
}
//这里有几个细节需要说明下
//*1 把所有点都加入到队列中,是因为不知道该从那个点起搜
//*2 一开始vv[i]=0,而不等于1时是因为这句话 if (vv[e[k].to]==node) return 1;
//   如果if (vv[e[k].to]>node) return 1;那么vv[i]=1;
//*3 v[now]=0这句话必须放在*3处是因为这个题的题意决定可能存在自环 
//   包括*4这句也是为了处理自环。当然去掉*4程序照样可以过,时间上也没有多大改变。
//   (因为去掉*4后由于存在自环,会不断的把该点加入队列尾的,最终会超过node次)
//   但是把v[now]=0的位置一移动,该程序就会对自环视而不见,最终导致WA
//  其实,最好的方法是主程序先判一下自环,这样v[now]=0的位置就无所谓了,*4这句也可以去掉

下面均假设在主程序先判一下自环后。

在基础代码上稍微改下//1000ms
bool ok(double x,int s)
{   int hea,tail,now;
    hea=0; tail=1; tr[1]=s; v[s]=1;
    while (hea!=tail)
    {   hea=(hea+1)%N;
        now=tr[hea];
        v[now]=0;
        vis[now]=1;
        for (int k=head[now];k!=-1;k=e[k].next)
             if (d[e[k].to]<d[now]+e[k].w-x)
             {  d[e[k].to]=d[now]+e[k].w-x;
                if (!v[e[k].to])
                     {vv[e[k].to]++;
                      if (vv[e[k].to]==node) return 1;
                      v[e[k].to]=1;
                      tail=(tail+1)%N;
                      tr[tail]=e[k].to;
                      v[e[k].to]=1;
 	     }
    }
}
//新增vis数组,vis[i]代表已被搜索过。
//为什么这样写就变快了这么多。。真心不懂啊。。。难道数据是一个块一个块的

下面是真正的优化了
优化一:
下一句是论文原话:
当元素进入队列的总次数之和超过T*(M+N)时,就判断图中存在正环。一般T=2
试了一下T=1,果然WA了
在基础代码上修改:
1. hea=0; tail=node; 后加int tot=0;
2. hea=(hea+1)%N;后加tot++;if (tot>max) return 1; //max为T*(M+N),可以先在主程序里算出
3. vv什么的都可以删掉了,记得处理自环。//删了更快
这样基本就挺进500-600ms


优化二:

//改写spfa_bfs为spfa_dfs,200+ms
bool spfa(double x,int now)
{    v[now]=1;
     for (int k=head[now];k!=-1;k=e[k].next)
             if (d[e[k].to]<d[now]+e[k].w-x)
             {  d[e[k].to]=d[now]+e[k].w-x;

                if (!v[e[k].to])
                    if  (spfa(x,e[k].to)) return 1;else;
                else return 1;
             }

    v[now]=0;
    return 0;
}
bool find(double mid)
{   memset(v,0,sizeof(v));
    memset(d,0,sizeof(d));
    for (int i=1;i<=node;i++)
       {
        if (spfa(mid,i)) return 1;
       }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值