关闭

[NOIp 2015] 对D1T2的一些拓展研究

标签: 图论
640人阅读 评论(0) 收藏 举报
分类:

<del>随便口胡,错误一大堆,欢迎打脸</del>

D1T2因为只有n条边,所以只存在简单环,那我们把这个问题复杂一下,每个人可能能告诉多个人,也就是边的数量大于n,那么这样的话就会出现复杂环了。如果还是直接用tarjan求的话肯定是错的,因为tarjan每次找的是一整个大环。在这里想到了一种比较简单的解法。

我们每次选一个没有走过的点开始DFS,并一路将走到的点压入存路径的栈中,同时用visit[i]表示i有没有走过,设当前的路径是p,

Visited[i]=0 …………i从未被访问过 精确的说是i可以访问

Visited[i]=1 …………i已经被访问过 且i在p中

Visited[i]=2 …………i已经被访问过 且i不在p中

对于所有visited[i]=2的点我们就没有必要访问了,说明:设当前点是now,下一个是next且visited[next]=2,假设next和now在一个环里的话,曾经访问next的时候就应该可以访问到now,但是事实上并没有访问到,所以说明next和now不在一个环里,那么这个点也就是没有意义,就可以不必去找next了。

在DFS的时候一路给点打上时间戳,用SPFA的思想,因为我们从一个固定的点出发,小环中的边比大环中的边少,那么如果一个点同时存在于小环和大环中的话,从小环走过来的时候时间戳肯定比大环走过来小,那么我们每次如果找到一个点,当前的时间戳大于已有的时间戳的话,就将这个点更新,重新设为可选。

如果我们某一次访问到一个点k,满足visited[k]=1,那么p就是一个完整的环,用p来更新答案,同时用上面SPFA的思想看能否更新k点,如果能更新就继续从k点往下走。

但是这样遇到了一个问题,众所周知SPFA的复杂度是O(EM)的,E是不确定的常数,也就是说在上文的方法中,如果某些点不断被更新,就很容易被卡掉,如下就是一个例子:

 

(用红点表示n/2+1号点)可以很明显看出,答案应该是1->2->红点->.....->n-1->n,但如果我们刻意控制一下读入方式,导致遍历的时候从1先到了2再到3一直到n/2号点,在n/2->红点的时候更新了一下红点的时间戳,于是红点变成可访问,花了O(n/2)的时间把剩下的点遍历了一遍,回来以后到n/2-1的时候又更新了一下红点,又花了O(n/2)的时间,这样一直下去,总共红点被更新了n/2次,那么这样的实际复杂度已经达到了O(n^2/4)的时间,在n=200000的时候是远远超过了限制。

我们的目的就是,优化,再优化!

对于这个问题,一种比较好的方法是A*或者估价函数,但如果是在考场上的话,随机化也不失为一种高效而简洁的方法。我们对于每个点,将连出去的边的顺序随机打乱然后再依次访问,对于上文的情况,如果一路下去一直访问到n/2的情况的可能性只有,考虑极限情况就是nlogn的复杂度,也就是运气很差的情况下,一直访问到logn号点,直到此时才开始更新,那么这样的概率是,也就是,n越大,超时的可能性反而越小,只有十万分之一,是在可接受范围内的。

但是如果RP爆炸,真的遇到这样的情况怎么办呢?

再优化!

可以发现,每次找到红点的时候,接下来的一段都是重复走的,一条链和一条边实际上是一个效果,所以我们考虑将每个单链都缩成一条边,如果再像上图那种情况的话,红点和1之间的链缩成一条边,复杂度直接降了一个O(n),总复杂度最坏也只有O(n),至此,这题的复杂度仍然是O(EM),但是E已经降成了一个很小的常数了。

不过还不够,我们还有更好的优化!

之前的visited[i]=2,说明i是一个不用访问的点,但事实上,我们还是枚举了i,对常数还是造成了一点影响,所以我们考虑可不可以让枚举的每个点都是有意义的点。

最终的结果是要找最小环,既然是个环,那我们就在环里找环,不在环里的点就直接抛弃掉。所以我们先做一遍tarjan,将所有的大环提取出来,然后再从每个环里按上文方法进行遍历,这样的话找最小环的复杂度就趋于稳定了。

总复杂度:O(N+M+KM),常数K在最坏情况下为10左右。

至此,这道题就基本解决了,是否还有更稳定更快的方法,仍然会继续探究。

<del>随便口胡,错误一大堆,欢迎打脸</del>

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:48461次
    • 积分:1475
    • 等级:
    • 排名:千里之外
    • 原创:102篇
    • 转载:2篇
    • 译文:0篇
    • 评论:11条
    Latex在线公式编辑器
    快点我
    最新评论