洛谷 P4017 最大食物链计数 题解(拓扑排序+记忆化搜索)

题目链接

题目思路

显然可以用记忆化搜索来做,但这个题目要我了解一下拓扑排序。但感觉拓扑排序好像没什么用

拓扑排序

在一个有向图中,对所有的节点进行排序,要求没有一个节点指向它前面的节点。

先统计所有节点的入度,对于入度为0的节点就可以分离出来,然后把这个节点指向的节点的入度减一。

一直做改操作,直到所有的节点都被分离出来。

如果最后不存在入度为0的节点,那就说明有环,不存在拓扑排序,也就是很多题目的无解的情况。

下面是算法的演示过程。

在这里插入图片描述

题目分析

这里具体讲一下为什么要用拓扑排序(思维过程):

1、这是一道图论题

2、不是求最短路

3、根据提示“最左端是不会捕食其他生物的生产者”可以想到,我们要入度为零的点开始查找

4、再看一遍题目,就是求路径数,当且仅当一个点的入度变为零时才需要入队,并不是数据更新一次就要入队

5、出度为零的点的路径总数和就是答案

f [ i ]表示到达i的路径数量

代码

#include
#include
using namespace std;
const int maxn=5e3+5;
const int mod=80112002;
int n,m,head[maxn],chu[maxn],ru[maxn],f[maxn],cnt,ans,u,v;
queue q;
struct node{
    int to,next;
}e[maxn*100];
void add(int u,int v){
    e[++cnt].to=v;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        add(v,u);
        chu[v]++,ru[u]++;
    }
    for(int i=1;i<=n;i++){
        if(!ru[i]){
            f[i]=1;
            q.push(i);
        }
    }
    while(!q.empty()){
        int fa=q.front();
        q.pop();
        for(int i=head[fa];i;i=e[i].next){
            int son=e[i].to;
            f[son]=(f[son]+f[fa])%mod;
            ru[son]--;
            if(!ru[son]){
                if(chu[son]){//还要继续
                   q.push(son);
                }else{//已经是生产者了
                    ans=(ans+f[son])%mod;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

参考文章
https://blog.csdn.net/qq_41713256/article/details/80805338
https://www.luogu.com.cn/blog/dbyblog/solution-p4017

你可能感兴趣的:(图论)