ccf 202212-2 训练计划 python满分

题目信息

试题编号202212-2
试题名称训练计划
时间限制1.0s
内存限制512.0MB

问题背景
西西艾弗岛荒野求生大赛还有 n 天开幕!

问题描述
为了在大赛中取得好成绩,顿顿准备在 n 天时间内完成“短跑”、“高中物理”以及“核裂变技术”等总共 m 项科目的加强训练。其中第 i 项(1≤i≤m)科目编号为 i,也可简称为科目 i。已知科目 i 耗时 ti 天,即如果从第 a 天开始训练科目 i,那么第 a+ti−1 天就是该项训练的最后一天。
大部分科目的训练可以同时进行,即顿顿在同一天内可以同时进行多项科目的训练,但部分科目之间也存在着依赖关系。如果科目 i 依赖科目 j,那么只能在后者训练结束后,科目 i 才能开始训练。具体来说,如果科目j 从第 a天训练到第 a+tj−1 天,那么科目 i 最早只能从第 a+tj 天开始训练。还好,顿顿需要训练的m 项科目依赖关系并不复杂,每项科目最多只依赖一项别的科目,且满足依赖科目的编号小于自己。那些没有任何依赖的科目,则可以从第 1 天就开始训练。
对于每一项科目,试计算:
1)最早开始时间:该科目最早可以于哪一天开始训练?
2)最晚开始时间:在不耽误参赛的前提下(n 天内完成所有训练),该科目最晚可以从哪一天开始训练?n天内完成所有训练,即每一项科目训练的最后一天都要满足 ≤n。需要注意,顿顿如果不能在n 天内完成全部 m 项科目的训练,就无法参加大赛。这种情况下也就不需要再计算“最晚开始时间”了。
——————————————————————————————————————————————
输入格式
从标准输入读入数据。 输入共三行。 输入的第一行包含空格分隔的两个正整数 n 和 m,分别表示距离大赛开幕的天数和训练科目的数量。 输入的第二行包含空格分隔的 m 个整数,其中第 i 个(1≤i≤m)整数 pi 表示科目 i 依赖的科目编号,满足 0≤pi<i;pi=0 表示科目 i 无依赖。
输入的第三行包含空格分隔的 m 个正整数,其中第 i 个(1≤i≤m)数 ti 表示训练科目 i 所需天数,满足 1≤ti≤n。
输出格式
输出到标准输出中。 输出共一行或两行。 输出的第一行包含空格分隔的 m 个正整数,依次表示每项科目的最早开始时间。如果顿顿可以在 n 天内完成全部 m 项科目的训练,则继续输出第二行,否则输出到此为止。 输出的第二行包含空格分隔的 m个正整数,依次表示每项科目的最晚开始时间。

解题思路
首先我们必须明确的是如何计算最早开始时间和最晚开始时间,它们的处理一定存在某种计算公式
最早开始时间,分两种情况
①如果它没有依赖课程的话,那么它的最早开始时间就是第一天
②如果有依赖课程的话,那么它最早也要到依赖的课程结束之后才能开始,也就是beg_e[p[i]]+t[p[i]],起初我想的是用递归实现,但是后来发现跟根本没这个必要,因为题目说了每个科目所依赖的科目的编号一定小于它自身的编号,那么从前往后的顺序就足以算出每个科目的beg_e

最晚开始时间,同样分两种请况
①如果它没有被依赖的话,也就是完全独立的存在,那么它的最晚开始时间就是n-t[i]+1
②如果它被依赖的话,那么它的最晚开始时间就取决于依赖它的科目的最晚开始时间,而且是最晚开始时间值最小的那个时刻,因为要保证依赖它的科目必须在期限内完成,那么就要考虑依赖它的科目最晚能从什么时候开始并且保证所有依赖它的科目都能完成,那么应该选取最小值,也就是说该科目的最晚开始时间的计算公式为min(son[i])-t[i],son也就是依赖该科目的科目的集合,这里直接用双重循环实现。
但是计算最晚开始时间时要注意的是,对每个科目的处理最好按倒序进行,因为依赖该科目的科目一定在该科目后边,一开始倒序处理的话,等处理到第一个科目,后边的科目都处理完毕了,就不需要再计算了

这个最早和最晚的计算其实很像关键路径算法对活动和事件的一系列属性的计算
可以参考关键路径算法的原理
链接:关键路径

代码实现

n,m=map(int,input().split())
p,t=[0],[0]
p.extend(list(map(int,input().split())))
t.extend(list(map(int,input().split())))
beg_e,beg_l=[0 for i in range(m+1)],[0 for i in range(m+1)]
#首先处理最早开始时间
for i in range(1,m+1):
    if p[i]==0:
        beg_e[i]=1
    else:
        beg_e[i]=beg_e[p[i]]+t[p[i]]
flag=2
#题目的意思是只关注每个科目都按照最早开始,是否可以按期完成
for i in range(1,m+1):
    if beg_e[i]+t[i]-1>n:
        flag=1
        break
se,sl='',''
for i in range(1,m+1):
    se+=str(beg_e[i])+' '
print(se.strip())
#其次处理最晚开始时间
#这里要注意的是,与最早开始时间不同
#在最早开始时间的计算中,每一个科目的最早开始时间依赖于它的前继科目
#在最晚开始时间的计算中,由于某科目是被别的科目依赖的,所以计算它的最晚开始时间时要考虑依赖它的科目能否如期完成
for i in range(m,0,-1):
    tmp=1e9
    for j in range(i+1,m+1):
        #寻找是否有依赖该科目的科目
        if p[j]==i:
            tmp=min(tmp,beg_l[j])
    #如果没有被依赖,那么最晚可以在最后期限减持续时间的时刻开始
    if tmp==1e9:
        beg_l[i]=n-t[i]+1
    #如果有被依赖,那么最晚可以从依赖它的科目中找出最晚开始的时刻最小的科目减去本身的持续时间的时刻开始
    else:
        beg_l[i]=tmp-t[i]
if flag==2:
    for i in range(1,m+1):
        sl+=str(beg_l[i])+' '
    print(sl.strip())

参考文章
链接: here

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

so.far_away

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值