一日一题(23)

链接:https://atcoder.jp/contests/dp/tasks/dp_g

图片.png

题意为求一个有向图无环图上的最长路径。

有向无环图(DAG)是一个重要概念。往往很多模型都可以当做一个DAG。比如学生学习课程,学习一门课程需要修若干前置课程,那么可以将前置课程和本课程连有向边;又比如工厂生产零件,一道工序必须在另一道工序前面,或者只有做完某些工序,才能进行本工序,那么同样可以按照逻辑关系连有向边。

考虑本题,我们可以采用暴力搜索方式,求出每个点出发的所有路径。但是这是指数级别复杂度的。又想到动态规划,用dp[i]表示到第i个点的最长路径。那么状态转移方程就是
dp[i]=\max_j \{dp[pre_j]+1 \}
这里pre_j表示直接连向i的所有节点——到达i,当然必须先经过pre_j之一。

但是还有一个重要的事情要考虑:更新顺序。这是因为,如果在dp[pre_j]都没有被确定的情况下,dp[i]是不能被计算的。我们必须在确定dp[i]之前,确定好dp[pre_j]

注意,这里的顺序,实际上也就是之前所说学生修课的顺序、工厂生产的顺序。通过拓扑排序算法,我们可以确定出这样的顺序——图中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。

拓扑排序思路很简单:

(1) 选择一个入度为0的顶点并输出之;

(2) 从网中删除此顶点及所有出边。

这里主要是维护每个点的入度,并且利用队列储存入度只为0的节点,只需要初次扫描表将入度为0的放入栈(队列)中。每一次删除边,将对应点的入度-1即可。

详细算法解析参考其他资料


from queue import Queue

n, m = map(int, input().split())

E = [[] for i in range(n + 1)]
ind = [0] * (n + 1)
dp = [0] * (n + 1)
Q = Queue()
for i in range(m):
    x, y = map(int, input().split())
    E[x].append(y)
    ind[y] += 1

for i in range(1, n + 1):
    if not ind[i]:
        Q.put(i)

while not Q.empty():
    u = Q.get()
    for i in E[u]:
        ind[i] -= 1
        dp[i] = max(dp[i], dp[u] + 1)
        if not ind[i]:
            Q.put(i)

print(max(dp))
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容