最大食物链计数 题解

文字讲解

题目分析:

首先 ,要知道这道拓扑排序题目的性质。

食物链中的生物 —— 节点

生物之间的关系 —— 有向边

为了方便描述,我们

最左端是不会捕食其他生物的生产者 叫做 最佳生产者

最右端是不会被其他生物捕食的消费者 叫做 最佳消费者

数据中不会出现环

那么,“最大食物链”就是左端是 最佳生产者 ,右端是 最佳消费者

思路引导

易得,想要找到一条 最大食物链 ,则起始点入度要为0,终点出度要为0。于是有:

既要记录入度,还要记录出度!

现在的问题就是,如何找到所有的最大食物链的数量

正解

我们拿起笔,在草稿纸上先画一个图做参考。那么我们就拿样例进行举例吧。

(我先将最佳生产者表上蓝色,最佳消费者表上红色)

发现:答案为 到所有最佳消费者路径条数的总和

(这里的路径总和不是 连向它有几条边 ,而是以它结束的 最大食物链 数量的总和)

对于上面的图,5号点的对应路径数量就应该是到他的三个点(2号、3号、4号)对应的路径数量。

而2号、3号、4号对应的路径数量就是到他们对应的点的路径数量了。

以此类推,对于任一点结尾的 最大食物链的 数量,取决于蓝色点。(对应关系在下图用绿色边已标注)

我们就只需要在蓝色点(最佳生产者)存入Topo队列时,标记这个蓝色点答案为 1 ,当删点时,将要删除的点的答案 累加到 当前点之中即可。

以第 i 个点结束的 最大食物链的 数量 = 以 指向第 i 个点的点 结尾的 最大食物链的 数量的和

一下是模拟操作过程:

那么最后5号点对应路径数就是5了

那么代码实现就很简单了!

上代码:

#include<bits/stdc++.h>//万能头,懒人必备
using namespace std;//标准名称函数库开启 

const int MAXN = 5000 + 10;//定义常量大小
const long long mod = 80112002;//定义最终答案mod的值 

int n,m;//n个点and边的关系数m
int in[MAXN];//记录每个点的入度
int out[MAXN];//记录每个点的出度
vector<int>nei[MAXN];//记录每个点相邻的点有哪些,用动态数组更加节省时间
queue<int>q;//拓扑排序模板队列
int ans;//答案
int num[MAXN];//记录到这个点的路径数量 

inline int read(){//快速读入
    int f = 1, x = 0;
    char c = getchar();

    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }

    while (c >= '0' && c <= '9')
    {
        x = x * 10 + c - '0';
        c = getchar();
    }

    return f * x;
} 

int main(){//开始......
    n = read(),m = read();//快速读入
    for(int i = 1;i <= m; i++){//循环m条边
        int x = read(),y = read();//输入左节点和右节点
        in[y]++,out[x]++;//右节点入度+1,左节点出度+1
        nei[x].push_back(y);//建立一条单向边
    }
    for(int i = 1;i <= n; i++){//初次寻找入度为0的点(最佳生产者)
        if(in[i] == 0){//符合要求
            num[i] = 1;//令到其的路径数量为1
            q.push(i);//压入队列
        }
    }
    while(!q.empty()){//只要还有入度为0的点
        int tot = q.front();//取出首点
        q.pop();//弹出
        for(int i = 0;i < nei[tot].size(); i++){//枚举这个点相邻的所有点
            int next = nei[tot][i]; //取出目前枚举到的点
            in[next]--;//将这个点的入度-1(因为目前要删除第tot个点)
            num[next] = (num[next] + num[tot]) % mod;//更新到next点的路径数量
            if(in[next] == 0)q.push(nei[tot][i]);//如果这个点的入度为0了,那么压入队列
        }
    }
    for(int i = 1;i <= n; i++){//寻找出度为0的点(最佳消费者)
        if(out[i] == 0){//符合要求
            ans = (ans + num[i]) % mod;//累加答案
        }
    }
    cout<<ans<<"\n";//输出
    return 0;//end
}

这道题主要磨炼思维。

蒟蒻求管理大大过

原文地址:https://www.cnblogs.com/CJYBlog/p/12198894.html

时间: 2024-11-05 22:47:00

最大食物链计数 题解的相关文章

【luogu P4017 最大食物链计数】 题解

题目链接:https://www.luogu.org/problemnew/show/P4017 DAG + DP #include <queue> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 500010; const int mod = 80112002;

luogu_4017【题解】最大食物链计数(拓扑排序)

题目:https://www.luogu.org/problemnew/show/P4017 由于这个题有严格的偏序关系(应该严格吧).明显就可以想到拓扑排序. 用吃与被吃建图.同时记录出度与入度. 求拓扑排序的同时.如果谁的出度为 0 .则证明这条链到头.ans++. 最后输出答案. 代码如下: 这是邻接表存图的,速度比较快. #include<bits/stdc++.h> #define sc(x) scanf("%d",&x) using namespace

SDOI2016排列计数 题解

最近学插头DP学得有点不舒服,然后学什么斯坦纳树也学不动,于是就来写写题解,正好有助于巩固一下所学内容 题意较为简单,就是要求对于一个\(1-n\)的序列的全排列中,正好有\(m\)个位置满足\(a[i]==i\)(称其为"稳定的")的排列个数. 很明显,我们可以先固定\(m\)个位置,使得这些位置都为"稳定的",那么还剩下\(n-m\)个位置.因为只能恰好有\(m\)个稳定的数,所以这\(n-m\)个数必须都不是稳定的.也就是说,这\(n-m\)个数必须都不在它们

P1144 最短路计数 题解 最短路应用题

题目链接:https://www.luogu.org/problem/P1144 其实这道题目是最短路的变形题,因为数据范围 \(N \le 10^6, M \le 2 \times 10^6\) ,所以直接用Dijkstra算法是不行的,可以使用 Dijkstra+堆优化 或者 SPFA算法来实现. 我这里使用 SPFA算法 来实现 (不会Dijkstra堆优化囧) 这道题目因为需要计数,所以需要在dist数组基础上再开一个cnt数组,其含义如下: \(dist[u]\) :起点 \(1\)

BZOJ1016:[JSOI2008]最小生成树计数——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=1016 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了. 无外乎两种:K算法和P算法(当然还有第三种但是我不会(滑稽) P算法没法解于是用K算法. 发现K算法的正确性后其实我们需要做的工作就

[ZJOI2010]数字计数 题解

题面 这道题是一道数位DP的模板题: 因为窝太蒟蒻了,所以不会递推,只会记忆化搜索: 首先,咋暴力咋来: 将一个数分解成一个数组,这样以后方便调用: 数位DP的技巧:(用1~b的答案)-(1~a的答案)就是(a~b的答案): 那么对于每个数码i,我们做两次dfs(分别以a为上界和以b为上界): 设正在搜索的数码是digit: 枚举每一位,当这位==digit时,便将答案+1,并记忆化: 然后就没了; 可是这样做忽略了两个重要的事情: 1.可能存在前导零: 2.目前搜到的数比目标值要大: 对于这两

codevs1074 食物链

1074 食物链 2001年NOI全国竞赛 时间限制: 3 s 空间限制: 64000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是“1 X Y”,表示X和Y是同类. 第二种说法是“2 X Y”,表示X吃

cojs 强连通图计数1-2 题解报告

OwO 题目含义都是一样的,只是数据范围扩大了 对于n<=7的问题,我们直接暴力搜索就可以了 对于n<=1000的问题,我们不难联想到<主旋律>这一道题 没错,只需要把方程改一改就可以了 首先我们考虑不合法的方案强连通分量缩点后一定是DAG 考虑子问题:DAG计数 做法可以参考<cojs DAG计数1-4 题解报告> 这里给出转移方程 f(n)=sigma((-1)^(k-1)*C(n,k)*2^(k*(n-k))*f(n-k)) 如果考虑上强连通分量缩点的情况呢? 我

POJ 1182 食物链 Union Find题解

Union Find就是所谓的并查集. 本题做的很无语,最后发现居然是输入搞错,一直WA. 不能使用循环接受输入,否则是WA的,气死人,浪费那么多时间就为了这个. 难点: 1 构建关系树 2 构建公式 3 快速更新公式 要抽象思维出什么对应什么的关系和上面是逆关系,就是利用0,1,2构建出父子节点之间的关系值,我是这样去思考构建出准确无误的公式的. 这样的抽象度是挺高的,需要多多训练. 关系到数学和Union Find,难度还是挺高的,网上很多人解法了. 我这里就增加一个按权值更新的优化算法,和