对一个权值可正可负的图SPFA判是否存在正环
首先引用一篇国家集训队论文《spfa的优化及应用》——姜碧野
给个百度传送门http://wenku.baidu.com/view/9e1231126edb6f1aff001f25.html
orz
POJ 2949(先将该题题意转化到二分答案判正环这一步)
下面是真正的优化了
优化一:
下一句是论文原话:
当元素进入队列的总次数之和超过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的优化及应用》——姜碧野
给个百度传送门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;
}