【Java】P4017最大食物链计数,动态规划+拓扑排序

题目总结

这是洛谷上的一道动态规划的提高题,题目本身不是很复杂,但是需要细心的注意每一个细节,首先题目上告诉我们"这里的“最大食物链”,指的是生物学意义上的食物链,即最左端是不会捕食其他生物的生产者,最右端是不会被其他生物捕食的消费者。"由此我们知道这是一个有向无环图,这个题肯定与图论的解题思路有着千丝万缕的关系,我们先了解题目,先不带入这个概念.

首先考虑动态规划,题目要求求出所有的食物链,我们就可以将问题具体为,所有的食物链的最顶层的捕食者的食物链总和,而抽离出每一个生物的食物链就可以很简单的总结为所有指向他的节点所拥有的链数之和,这么说可能比较抽象,举例子,例如输入

5 7 //五个生物 7行捕食情况
1 2 //1号生物被2号生物捕食,以下类推
1 3
2 3
3 5
2 5
4 5
3 4

可以画出连通图为

黄色的节点为出度为0的节点,也就是食物链最顶端的生物,他的所有连向他的路径的总和就是所有的食物链

那么我们求得一个点的所有的链的总和就是所有指向这个节点的节点的路径和,那么我们可以使用动态规划通过状态转移方程dp[i]=sum(dp[x]):x就是所有指向i节点的节点,我们通过图上可以看出,如果要保持当前状态可以通过最优子结果来获得的话,就要保持子问题的无后效性,所以我们需要以一个正确的循序去地推这个结果,需要用拓扑排序,将所有入读为0的节点去扫描所有他指向的节点,对其他的节点的入读进行递减,具体的代码可以看下图,已通过全部AC

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class SearchFoodChain{
    public static void main(String[] args) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String[] nm = reader.readLine().split(" ");
        int n = Integer.parseInt(nm[0]);
        int m = Integer.parseInt(nm[1]);
        List<List<Integer>> graph = new ArrayList<>();
        int[] in = new int[n];
        int[] out = new int[n];
        int[] line = new int[n];
        for(int i = 0;i<n;i++)
            graph.add(new ArrayList<>());
        for(int i = 0;i<m;i++){
            String[] temp = reader.readLine().split(" ");
            int r = Integer.parseInt(temp[0])-1;
            int c = Integer.parseInt(temp[1])-1;
            graph.get(r).add(c);
            in[c]++;
            out[r]++;
        }
        Queue<Integer> queue = new LinkedList<>();
        for(int i =0;i<n;i++){
            if(in[i]==0){
                queue.offer(i);
                line[i]=1;
            }

        }
        int mod = 80112002;
        int ans = 0;
        while(!queue.isEmpty()){
            int check = queue.poll();
            List<Integer> list = graph.get(check);
            for(int now : list){
                line[now]+=line[check];
                line[now]%=mod;
                if(--in[now]==0){
                    if(out[now]==0) {
                        ans += line[now];
                        ans%=mod;
                    }
                    else
                        queue.offer(now);//出度为0的节点不会再对其他的节点的入度产生影响,所以不需要入队
                }
            }
        }
        System.out.println(ans%mod);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值