Skip to content

P1113 [USACO02FEB] 杂务(黄色 普及/提高-) ​

dp, topological order, https://www.luogu.com.cn/problem/P1113

John 的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。

当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务 1。

John 有需要完成的 n 个杂务的清单,并且这份清单是有一定顺序的,杂务 k (k>1) 的准备工作只可能在杂务 1 至 k−1 中。

写一个程序依次读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定 John 的农场有足够多的工人来同时完成任意多项任务。

输入格式

第 1 行,一个整数 n (3≤n≤10,000),必须完成的杂务的数目;

第 2 至 n+1 行,每行有一些用空格隔开的整数,分别表示:

  • 工作序号(保证在输入文件中是从 1 到 n 有序递增的);
  • 完成工作所需要的时间 len (1≤len≤100);
  • 一些必须完成的准备工作,总数不超过 100 个,由一个数字 0 结束。有些杂务没有需要准备的工作只描述一个单独的 0。

保证整个输入文件中不会出现多余的空格。

输出格式

一个整数,表示完成所有杂务所需的最短时间。

输入输出样例 #1

输入 #1

7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0

输出 #1

23

这是一个典型的有向无环图(DAG)上的动态规划(DP)或拓扑排序问题。

解题思路

  1. 依赖关系与拓扑序: 题目中明确指出:“杂务 k (k>1) 的准备工作只可能在杂务 1 至 k−1 中”。这意味着输入给出的任务顺序已经天然满足拓扑序。我们不需要显式地进行拓扑排序,只需要按照任务编号从 1 到 n 顺序计算即可。

  2. 状态定义: 设 dp[i] 表示完成第 i 个杂务所需的最短时间(即该任务的最早完工时间)。

  3. 状态转移方程: 对于任务 i,它必须在它所有的准备工作完成之后才能开始。因此,它的开始时间取决于其所有准备工作中完工时间最晚的那一个。

    dp[i]=leni+max({dp[j]∣j 是 i 的准备工作}∪{0})

    如果任务 i 没有准备工作,则对应的最大值为 0。

  4. 最终答案: 所有任务都完成的最短时间为所有任务完工时间的最大值,即 max1≤i≤ndp[i]。

Python 代码实现

python
import sys

def solve():
    # 读取所有输入数据
    input_data = sys.stdin.read().split()
    if not input_data:
        return
    
    n = int(input_data[0])
    
    # dp[i] 表示完成第 i 个杂务的最短时间
    dp = [0] * (n + 1)
    
    ptr = 1
    for _ in range(n):
        u = int(input_data[ptr])
        length = int(input_data[ptr+1])
        ptr += 2
        
        max_prev_dp = 0
        # 读取准备工作,直到遇到 0 结束
        while True:
            pre = int(input_data[ptr])
            if pre == 0:
                ptr += 1
                break
            if dp[pre] > max_prev_dp:
                max_prev_dp = dp[pre]
            ptr += 1
            
        # 转移方程
        dp[u] = length + max_prev_dp
        
    # 最终输出完成所有任务所需的最短时间
    print(max(dp))

if __name__ == '__main__':
    solve()

复杂度分析

  • 时间复杂度:每个任务和每个依赖关系都只会被遍历一次。题目规定每个任务的准备工作总数不超过 100 个,因此总时间复杂度为 O(n),在 n=10,000 的情况下运行效率较高。
  • 空间复杂度:需要一个大小为 n+1 的数组来存储每个任务的 dp 值,空间复杂度为 O(n)。