消圈算法c语言,【图论】Floyd消圈算法

本文介绍了Floyd消圈算法在判定链表是否存在环及其长度问题中的应用。通过快慢指针的方法,阐述了算法的原理和实现,并证明了其时间复杂度为O(n)。此外,还讨论了如何找到环的起点,并提供了一个示例问题,说明了Floyd消圈算法在解决数字循环问题上的实用性。

Definition&Solution

对于一个给定的链表,如何判定它是否存在环以及环的长度问题,可以使用Floyd消圈算法求出。

从某种意义上来讲,带环的链表在本质上是一个有向图

考虑下面的事实:假定小Y和小Z在圆形操场上跑步,小Z的速度是小Y的两倍,那么总存在一个时刻,使得小Z和小Y在同一个位置但是小Z比小Y多跑了若干圈。

该算法的复杂度为O(n)。

代码如下:

voidfloyd_c() {int c1=list_begin,c2=list_begin;//c=child

do{

c1=nxt[c1];

c2=nxt[c2]; dosth();

c2=nxt[c2]; dosth();

}while(c1!=c2);return;

}

其中dosth应具体情况具体分析。

考虑如何求出该环的起点。

如果你不理解环为什么有起点,不妨观察下面的链表:

9b5e783acc731bf713d05d98bba01fbf.png

由于1是链表的起点,以1号点为起点遍历整张链表,是从3号点进入原谅环的,我们认为3号点是原谅环的起点。

如何求出起点。

将小Z和小Y都放到链表的起点上,当两者相遇时,将其中一个指针再次放到起点上,二者再次相遇的位置就是环的起点。

证明如下:

由于含有多个环的链表可以通过数学归纳由含有一个环的链表证明,故不妨设所研究的链表只有一个环,同时还有一条长度至少为1(一个点)的链。

设环的长度为n,链的长度为m。快慢指针第一次相遇在环上的第k个点,那么有:

快指针的路程Sa=m+A*n+k,其中A∈Z,代表走了几圈①

慢指针的路程Sb=m+B*n+k。其中B∈Z,代表走了几圈②

由于快指针的速度是慢指针的两倍,即:

Va=2Vb③

在运动时间一定的情况下有:

Sa=2Sb④

①-②得:

Sa-Sb=(A-B)*n⑤

联立④⑤解得Sb=(A-B)*n⑥

由于A,B∈Z,且显然A>B,所以Sa,Sb都是圈长度的倍数。

不妨设Sb=T*n。显然从起点出发,走过T*n+m时,到达环的起点。

那么在两个指针相遇时,我们让两个指针的速度都为1,走m个单位就可以到达环的起点,这时快指针的路程为2Sb+m。慢指针的路程为Sb+m。

由于Sb=T*n,这时的路程可以理解为快指针走到m处然后绕了几圈,绕圈路程为2Sb,慢指针从路程为Sb的位置走了m,那么二者的相遇的地方显然是环的起点。

特别的,因为vb=1。所以Sb即为循环的次数。通过⑥式已经证明,该算法的复杂度为O(n)。其中常数随环的形态而改变。

Sample

Description

有一个非常无聊的傻逼,有一天他闲的没事干玩一个老式计算器,这个计算器只能显示答案最高的n位。比如n=2,计算99+1的答案为100时,显示为10。

现在他一开始输入了一个数字k,保证在能显示的范围内,然后将k平方,然后将答案平方,再平方……

这个无聊的人想知道屏幕上显示的最大数时多少。

Input

第一行是数据组数,对于每组数据,包含:

一个整数n,一个整数k

Output

对于每组数据,输出:

对应答案

Sample Input

2

1 6

2 99

Sample Output

9

99

Hint

n≤9

Solution

简单的数学归纳可以证明计算器上的数字是会出现循环的,可以使用Floyd消圈算法解决此题。

Code

#include

#define rg register

#define ci const int

#define ll long long intinlinevoid qr(int &x) {char ch=getchar(),lst=NULL;while(ch>'9'||ch='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();if (lst=='-') x=-x;

}char buf[20];

inlinevoid write(int x,const char aft,const boolpt) {if(x<0) {putchar('-');x=-x;}int top=0;do{

buf[++top]=x%10+'0';

x/=10;

}while(x);while(top) putchar(buf[top--]);if(pt) putchar(aft);

}

templateinline T mmax(const T &a,const T &b) {if(a>b) return a;returnb;}

templateinline T mmin(const T &a,const T &b) {if(a

templateinline T mabs(const T &a) {if(a<0) return -a;returna;}

templateinlinevoid mswap(T &a,T &b) {T temp=a;a=b;b=temp;}intt,n,k,ans;intnxt(ci);intmain() {

qr(t);while(t--) {

n=k=0;qr(n);qr(k);int c1=k,c2=k;ans=k;do{

c1=nxt(c1);

c2=nxt(c2);ans=mmax(ans,c2);c2=nxt(c2);ans=mmax(ans,c2);

}while(c1!=c2);

write(ans,'',true);

}return 0;

}short bufff[100];int nxt(intx)

{if(!x)return 0;long long kf=(long long)x*x;int L=0;while(kf>0) bufff[L++]=kf%10,kf/=10;int temp=n;if(temp>L) temp=L;int sum=0;for(int i=0;i

}

Summary

1、Floyd消圈算法是达到理论下限的判断有向图上环的算法。尽管它的常数难以控制,但是十分的实用并且好写。

2、在证明的最后使用了等效替代法,在其他的OI毒瘤题中,也应注意该方法的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值