message[时间标记]

来源:互联网 发布:揭秘淘宝刷到单流程图 编辑:程序博客网 时间:2024/06/07 04:41
题意分析:
    在一个有向图中,含有N个点和N条边,求出在这个图中最小环的长度。
思路分析:
    时间标记是一种通过类似DFS的方法,但是在其中记录了每一个点的访问次序,通过记录访问次序,来求环。从任意一个节点开始,每访问一个节点,就给它记录当前的时间标记。当访问到一个节点,它的时间标记已经被记录了,就证明至少会形成一个环。而在这个环当中,会出现两种情况,如果另一个节点已经和它产生了一个环,而这一个节点是插入进这个环的,这一个环的大小自然就肯定不是最小的。而如果它在这一次访问的开始点之后,这个节点必然是形成的一个在当前的访问中,产生的一个最小的环。再讲这个环的长度求出,与最小值比较,就得出最终的最小值。
那么,环的长度需要怎么求呢?
根据记录的时间标记,访问到之前访问过的节点,就证明找到了一个环。而这个环的长度,就是指向前面的节点的那个节点的时间标记与它指向的节点的差+1,就是这个环的长度。
如图所示:
message[时间标记] - 赵子睿 - 赵子睿的博客
 从第一个节点开始,一直访问到第四个,接着又访问到了第二个节点,而且这一个节点是在1之后访问的,证明这是一个环。求出环的长度为3。接着,访问节点5,一直访问到节点2,发现之前已经访问过了,就表示会访问到前面的一个环,所以就放弃。
时间复杂度:不超过O(2N),约等于O(N)
参考程序:
#include<stdio.h>
#include<stdlib.h>

#include<iostream>
using namespace std;

const int MAXN=200100;

int ans,n;
int f[MAXN],next[MAXN];

int main()
{
freopen("message.in","r",stdin);
freopen("message.out","w",stdout);

scanf("%d",&n);
for(int i=1;i<n+1;i++)
scanf("%d",&next[i]);

int ti=1;ans=n;//ti是时间标记,当前的图的答案的最大值一定是节点的个数
for(int i=1;i<n+1;i++)
if(f[i] == 0)//如果当前节点没有访问
{
int start=ti,k=i;//start记录开始节点,k作为指针

while(f[k] == 0)//只有k号节点没有访问过是运行
{
ti++;
f[k]=ti;
k=next[k];

if(f[k] > start) //如果那一个节点是访问过的,而且是在start之后,就证明是环
ans=min(ti-f[k]+1,ans);//求出最小值
}
}

printf("%d\n",ans);
return 0;
}

0 0
原创粉丝点击