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

· · 题解

图的表示: 由于n的数据规模较大,并且m远远小于n*n,因此,我们采用邻接 表来表示这个图。

寻找食物链: 不难发现,我们从生产者开始,沿着食物链往上搜索,一直搜索 到最高的消费者,那么这就是一条食物链。

生产者和消费者: 那么,我们如何判定生产者和最高级消费者,也即是说如何寻找 搜索的开始和结束?很显然,这个食物网的图是一个有向图,生 产者是其中的没有任何其他节点指向它的结点,而最高消费者是 其中没有指向任何其他结点的点。

判断最高消费者: 显然,最高消费者是很好判断的,当我们搜索到了一个结点,发 现它已经没有指向任何其他的结点让我们继续搜索,那么我们就 找到了一条食物链,并且返回。

判断生产者: 生产者可以在我们建图的时候判断,我们用一个布尔数组来记录 一个结点是否是生产者,首先将这个数组的值全部初始化为 true,然后在建立图的过程中,如果建立一条a指向b的边,那么 显然,我们应该把b置为false,因为已经有一个a指向了它,它 不可能是生产者了。

深度优先搜索: 这里我们采用深度优先搜索,遍历我们的布尔数组,如果是生产 者,我们便开始搜索,当搜索到了最高消费者,我们便找到了一 条食物链,于是ans++(ans用于保存最终结果),遍历完所有的生 产者,我们也就找到了所有的食物链。

记忆化搜索: 如果按照上面的朴素的搜索,很不幸会tle,因为虽然我们只有 1e5的结点数,但是食物链有大量的交叉,也就是我们会重复搜索 很多已经搜索过的结点,这时,我们就需要采用记忆化的搜索, 已经搜索过的结点的值可以直接用,而不用再重复进行一次搜 索。

我们用一个dp数组来记录之前的搜索结果,dp数组的含义为 1.若dp[i] = -1 ,则表示编号为i的结点未被搜索,需要进行一 次搜索 2.若dp[i] != -1 ,则表示假设编号为i的结点为生产者(其实并 不是),从i开始的食物链的数量

那么,我们就不难发现,从编号为i的结点开始引出的食物链的数 目,就是所有以它指向的结点为生产者的食物链条数的和

下面我们就可以写代码了

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define MOD 80112002
struct node
{
    int n;   //结点点标号
    node * next;  //指向下一个结点的指针
};//定义链表结点
node v[100005];//邻接表
bool isStart[100005];//判断标号为i的结点是否为生产者
int dp[100005];//保存之前的搜索结果,若为-1,表示需要进行一次搜索,否则表示从
//该节点开始(以它为生产者,其实它不是)的食物链的条数
void insertnode(int a,int b)
{//插入一条a指向b的边,用于建立邻接表
    node * t;
    t = new node; // new一个结点
    t->n = b;   // 将结点的点标号置为b
    t->next = v[a].next;  //将之插入邻接表
    v[a].next = t;        //将之插入邻接表
    isStart[b] = false;  //由于a指向b,b不再是生产者,因此将布尔数组置为false
    return;
}
int dfs(int x)
{//记忆化搜索,搜索结点的标号为x
    int ans;//保存返回值
    if(dp[x]!=-1)//如果不为-1,则已经搜索过,可以直接用之前的搜索结果
        return dp[x];//返回之前保存在dp中的结果
    else if(v[x].next == NULL)
    {//如果没有指向其他任何结点,那么是最高消费者,那么从它开始的食物链数目为1
        dp[x] = 1;//将dp[x]置为1
        return 1;//返回以x为生产者的食物链数目,即1
    }
    node * p;
    p = v[x].next;
    ans = 0;//将返回值先初始化为0
    while(p!=NULL) //遍历所有x指向的结点
    {
        ans += dfs(p->n);
        ans %= MOD;
        /*
        从编号为i的结点开始引出的食物链的数目,就是所有以它指向的结点为生产者的食         物链条数的和
        */
        p = p->next;
    }
    dp[x] = ans; //将搜索结果保存在dp数组当中
    return dp[x]; // 返回搜索结果
}
int main()
{
    int n,m,a,b,ans;

    while(cin >> n >> m) //输入n和m
    {
        for(int i=1;i<=n;i++)
        {
           /*
           将邻接表数组初始化,并且将布尔数组全            部置为true,将dp数组置为-1
                */
            v[i].n = i;
            v[i].next = NULL;
            isStart[i] = true;
            dp[i] = -1;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            insertnode(a,b);//将边插入邻接表
        }
        ans = 0;
        for(int i=1;i<=n;i++)
        {
            if(isStart[i]&&v[i].next!=NULL)
            /*如果是生产者并且不是单独的一种孤立生物,那么对它进行搜索*/
                ans += dfs(v[i].n) ;
                ans %= MOD;
        }
        printf("%d\n",ans);
    }
    return 0;
}