P4017 最大食物链计数(洛谷)
老师开始帮我们查漏补缺啦!
我们的老师这两天给了我们一些我们没怎么学的函数和算法,比如STL的函数和拓扑排序之类的,这个题就是讲拓扑排序的。
先看题板:
题目背景 你知道食物链吗?Delia 生物考试的时候,数食物链条数的题目全都错了,因为她总是重复数了几条或漏掉了几条。于是她来就来求助你,然而你也不会啊!写一个程序来帮帮她吧。 题目描述 给你一个食物网,你要求出这个食物网中最大食物链的数量。 (这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。) Delia 非常急,所以你只有 11 秒的时间。 由于这个结果可能过大,你只需要输出总数模上 80112002 的结果。 输入格式 第一行,两个正整数 n、m,表示生物种类 n 和吃与被吃的关系数 m。 接下来 mm 行,每行两个正整数,表示被吃的生物A和吃A的生物B。 输出格式 一行一个整数,为最大食物链数量模上 80112002 的结果。 输入输出样例 输入 #1复制 5 7 1 2 1 3 2 3 3 5 2 5 4 5 3 4 输出 #1复制 5 说明/提示 各测试点满足以下约定:
n<=5000
嗯,看起来不错。直接暴力找出没有天敌的动物,然后操作就可以了。
我们先看辅助斯烤的关系图(仅限样例)
题目描述告诉我们,食物链的开始点是没有天敌的动物。我们就枚举一遍所有点的入度,看见有一个是0的,就把他插进队列。
然后我们从他吃的食物里找一个,把这个食物的天敌数量-1,把食物的路径变量+=自己的路径变量(一个节点的路径变量是指:以他为末尾,有多少种包含他的食物链),如果这个食物的天敌数变成0了,就说明他的路径变量完整了。这样就可以继续搜索了。
如果一个节点没有食物就饿死了就说明他是结尾了,而且他的天敌数如果同时是0的话,就表明已经可以统计食物链条数了。(已经知道以他为末尾,有多少种包含他的食物链,而且这个节点没有食物,那就结束了。)
好了,需要主要思想已经讲完了,看看代码吧:
#include<iostream> #include<cstdio> #include<queue> using namespace std; int a,b,n,m,bj[5005][5005],zshu,shu;//对了,用longlong的话空间会炸掉的哦(longlong是8个字节,比int多一倍) struct hehe { int as,bs,qz; }sz[5005];//sz[i]表示i的情况(比如天敌有几个,食物有几个,强制末尾是他的食物链有几个) queue<int>q; int main() { scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); sz[a].bs++;//bs是食物数 sz[b].as++;//as是天敌数 bj[a][b]=1;//bj[i][j]=1表示i会吃掉j } for(int i=1;i<=n;i++)//查找食物链的开始节点 { if(sz[i].as==0)//找到啦 { sz[i].qz=1; q.push(i); } } while(q.empty()!=true)//只要还有一个节点,while就休想退出! { shu=q.front(); for(int i=1;i<=n;i++) { if(bj[shu][i]==1)//研究表明:第shu号动物会吃掉第i号动物 { sz[i].as--;//把他的天敌-1(就是还需要收集多少个节点的路径数) sz[i].qz+=sz[shu].qz;//qz是路径总数的意思。 sz[i].qz%=80112002;//题目告诉我们要取模(这里不取会40分,别问我怎么知道的) if(sz[i].as==0)//符合要求,要不是结尾,要不是下一个。 { if(sz[i].bs==0)//他是结尾 { zshu+=sz[i].qz;//zshu是食物链的总数 zshu%=80112002;//正常操作,取模 continue;//他都结尾了加他干嘛? } q.push(i);//他不是结尾,加进去继续搜 } } } q.pop();//删除第一个,也就是现在这个 } cout<<zshu<<endl;//输出 return 0; }
这个题就很健康的讲完了,感觉还行。(感觉拓扑排序不难的样子,希望是这样)