[USACO08DEC]Trick or Treat on the Farm 记忆化搜索

这一题非常水,因为每个点的下一个目的地是唯一的,可以考虑对每一个还为访问过的点dfs直接找出所有的环,同时更新每一个点能去的点的数量(即答案)。

我们dfs时找到环上已经遍历过的一个点,用当前的dfn(即节点深度来算出环的大小,并在回溯时更新环上所有点的值(都是环的大小),知道回溯完整个环为止。如果一个点没有在环上,他的tot值应该等于他的目的地的tot值加一。

代码如下:

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff

using namespace std;

int to[100001];
int dfn[100001];
int tot[100001];
int n,t,f;

inline int qr(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=(res<<1)+(res<<3)+(ch^48);
    return res;
}

bool dfs(int i){
    if(to[i]==i)tot[i]=1;return 0;//特判
    if(tot[i])return 0;
    if(dfn[i])return 1;
    dfn[i]=++t;
    if(dfs(to[i])){
        if(f==dfn[i]){
            tot[i]=tot[to[i]];
            f=0;return 0;
        }//回溯完整个环了,return的值应该变回0
        if(!f){
            tot[i]=dfn[i]-dfn[to[i]]+1;
            f=dfn[to[i]];return 1;
        }//算环的大小
        tot[i]=tot[to[i]];
        return 1;//目前还在环上遍历
    }tot[i]=tot[to[i]]+1;
    return 0;//没在环上
}

int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    n=qr();
    for(int i=1;i<=n;++i)
        to[i]=qr();
    for(int i=1;i<=n;++i){
        if(!tot[i])dfs(i);
        printf("%d
",tot[i]);
    }
    return 0;
}

✐☎博主撰文不易,转载还请注明出处;若对本文有疑,请私信或在下方讨论中提出。O(∩_∩)O谢谢!☏

☃〔尽管小伙伴们肯定有千百种方式针对,但博主还是极其非常十分不要脸的把反对键吃掉辣!〕☃

✿『$At$ $last$:非常一(hu)本(shuo)正(ba)经(dao)的:博主很笨,请不要欺负他』✿✍

原文地址:https://www.cnblogs.com/812-xiao-wen/p/9879881.html