CSP 202104-4 校门外的树 python 动态规划DP + 约数优化

简介: CSP 202104-4 校门外的树 python 动态规划DP + 约数优化

CSP 202104-4 校门外的树 python 动态规划DP + 约数优化


题目连接


题目描述


X 校最近打算美化一下校园环境。前段时间因为修地铁,X 校大门外种的行道树全部都被移走了。现在 X 校打算重新再种一些树,为校园增添一抹绿意。


X 校大门外的道路是东西走向的,我们可以将其看成一条数轴。在这条数轴上有 n 个障碍物,例如电线杆之类的。虽然障碍物会影响树的生长,但是障碍物不一定能被随便移走,所以 X 校规定在障碍物的位置上不能种树。n 个障碍物的坐标都是整数;如果规定向东为正方向,则 n 个障碍物的坐标按照从西到东的顺序分别为 a1,a2,⋯,an。X 校打算在 [a1,an] 之间种一些树,使得这些树看起来比较美观。


X 校希望,在一定范围内,树应该是等间隔的。更具体地说,如果把 [a1,an) 划分成一些区间 [ap1,ap2),⋯,[apm−1,apm)(1=p1<p2<⋯<pm=n),那么每个区间 [api,api+1) 内需要至少种一棵树,且该区间内种的树的坐标连同区间端点 api,api+1 应该构成一个等差数列。不同区间的公差,也就是树的间隔可以不相同。


例如,如果障碍物位于 0,2,6 这三处,那么我们可以选择在 [0,2) 和 [2,6) 分别种树,也可以选择在 [0,6) 等间隔种树。如果是分别在 [0,2) 和 [2,6) 种树,由于每个区间内至少要种一棵树,坐标 1 上必须种树;而 [2,6) 上的树可以按照 1 的间隔种下,也可以按照 2 的间隔种下。下图表示了这两种美观的种树方案,其中橙色的圆表示障碍物,绿色的圆表示需要在这个位置种树,箭头上的数字表示种下这棵树时对应的间隔为多少。


684c963e09efa3eef439d573b3e3f5e5.png


对区间 [0,2) 和 [2,6) 分别以 1 和 2 的间隔种树是美观的



caa0615708c910d5ba7542cca8927fac.png


对区间 [0, 2) 和 [2, 6) 分别以 1 的间隔种树也是美观的 而如果选择在 [0,6) 区间等间隔种树,我们只能以 3 的间隔种树,因为无论是选择间隔 1 或者间隔 2,都需要在坐标 2 上种树,而这个位置已经有障碍物了。下图分别表示了间隔为 3,2,1 时的种树情况,红色箭头表示不能在这里种树。


5740061932fb410a34663da8e8be7e90.png

对区间 [0, 6) 以 3 的间隔种树是美观的



4626675c4be3ab8af6ba4ef784e31cec.png


图 4: 对区间 [0, 6) 以 2 的间隔种树是不美观的



d6eaf29cf541c301ddf1bfcb5b955d2a.png


图 5: 对区间 [0, 6) 以 1 的间隔种树也是不美观的

一般地,给定一个区间 [al,ar),对于树的坐标的集合 T⊂(al,ar)(T⊂Z),归纳定义 T 在 [al,ar) 上是美观的:


如果 T≠∅,T∩{al,al+1,⋯,ar}=∅,并且存在一个公差 d≥1,使得 T∪{al,ar} 中的元素按照从小到大的顺序排序后,可以构成一个公差为 d 的等差数列(显然,这个等差数列的首项为 al,末项为 ar),则 T 在 [al,ar) 上是美观的;

如果 T∩{al,al+1,⋯,ar}=∅,并且存在一个下标 m(l<m<r),使得 T∩(al,am) 在 [al,am) 上是美观的,且 T∩(am,ar) 在 [am,ar) 上是美观的,则 T 在 [al,ar) 上是美观的。

根据这一定义,空集在任意区间上都不是美观的;另外,如果存在下标 i 使得 ai∈T,那么 T 一定不是美观的。


我们称两种种树的方案是本质不同的,当且仅当两种方案中,种树的坐标集合不同。请帮助 X 校对 [a1,an) 求出所有本质不同的美观的种树方案。当然,由于方案可能很多,你只需要输出总方案数对 10^9+7 取模的结果。


输出格式


输出到标准输出。

输出一个非负整数,表示本质不同的美观的种树方案的数量对 109+7 取模的结果。


样例输入1

3
0 2 6

样例输出1


3


样例说明1


这组样例即为题面描述中提到的那组。


样例输入2

11
0 10 20 30 40 50 60 70 80 90 10

样例输出2


256507


样例输入3


333
33 44 67 210 528 762 873 984 1234 1466 1739 2859 3421 4061 4598 5172 5201 5220 5261 532


样例输出3


7094396


思路


这道题需要用到动态规划DP,并且f[0]=1


设f [ i ] 为前i个障碍物所能生成的最多可能性

设  cnt[i][j]为从第i个障碍物到第j个障碍物的可行方案数

想要求cnt[i][j]只需从位置i枚举所有间隔的可能性, 看是否能在不触碰到 i , j 之间的障碍物的情况下到达


我们就可以写出我们的动态转移方程


image.png



比如我们求f[2]的时候,就是


image.png


动态规划的话只能够拿到60分,但是如果要拿满分,还需要进行约数优化的方法。


除此之外,得到我们的cnt数组的时候,第j个障碍物到第i个障碍物之间的方案数,注意这里是把a[j]和a[i]之间看作一个整体进行植树,不考虑分割情况,即在此区间里所有的树和a[j]、a[i]构成等差数列。如果只是穷举的话,我们就会超时,所以这里需要进行约数优化的方法。


约数优化:可以想到第j个障碍物到第i个障碍物之间 植树间隔 必须为 a [ i ] − a [ j ] 的 因子,方案数一定小于等于因子个数,因为这个间隔还不能撞上 a [ j ]和 a [ i ] 之间的障碍物。我们倒着从 i − 1  开始枚举 j ,这样一开始$ i - 1$ 和 $i 之 间 是 没 有 障 碍 物 的 , 则 之间是没有障碍物的,则之间是没有障碍物的,则a[i] - a[i - 1] 的 所 有 因 子 都 满 足 条 件 ; 然 后 到 了 ]的所有因子都满足条件;然后到了]的所有因子都满足条件;然后到了i - 2, 同 样 先 枚 举 ,同样先枚举,同样先枚举a[i] - a[i - 2]的 所 有 因 子 , 这 些 因 子 中 已 经 处 于 集 合 中 的 一 定 不 可 , 等 于 的所有因子,这些因子中已经处于集合中的一定不可,等于的所有因子,这些因子中已经处于集合中的一定不可,等于a[i]-a[i-1]的 也 不 可 , 因 为 按 这 样 的 间 隔 排 列 一 定 会 有 树 遇 上 的也不可,因为按这样的间隔排列一定会有树遇上的也不可,因为按这样的间隔排列一定会有树遇上a[i - 1]这 个 障 碍 物 ; 以 此 类 推 , 倒 着 枚 举 到 这个障碍物;以此类推,倒着枚举到这个障碍物;以此类推,倒着枚举到a[i] - a[k]因 子 的 时 候 , 如 果 已 经 在 之 前 的 枚 举 中 使 用 过 , 则 跳 过 , 否 则 方 案 数 就 加 一 。 我 们 使 用 一 个 状 态 数 组 因子的时候,如果已经在之前的枚举中使用过,则跳过,否则方案数就加一。我们使用一个状态数组因子的时候,如果已经在之前的枚举中使用过,则跳过,否则方案数就加一。我们使用一个状态数组st[M]$,保存某个因子是否已经使用过了,。


代码


60分 运行超时


第一个方法代码只能过60%,得到60分就运行超时了

# http://118.190.20.162/view.page?gpid=T125
n = int(input())
a = list(map(int,input().split()))
N = 1010
MOD = int(1e9+7)
def get_cnt(a,b):
    res = 0
    maxn = b - a
    for i in range(1,maxn+1): # 枚举间隔
        if a + i == b: continue
        for j in range(a+i,b+1,i): # 不断进行枚举
            if j > b: # 没有到达b
                break
            if j == b:
                res += 1
                break
            if st[j]:
                break
    return res
'''
设f[i]为前i个障碍物所能生成的最多可能性
设 cnt[i][j]为从第i个障碍物到第j个障碍物的可行方案数
想要求cnt[i][j]只需从位置i枚举所有间隔的可能性,看是否能在不触碰到i,j之间的障碍物的情况下到达
'''
f = [0]*N
f[0] = 1
cnt = [[0]*N for _ in range(N)]
from collections import defaultdict
st = defaultdict(int)
for i in range(n):
    st[a[i]] = 1
for i in range(n-1):
    for j in range(i+1,n):
        cnt[i][j] = get_cnt(a[i],a[j])%MOD
for i in range(1,n):
    for j in range(0,i):
        f[i] = (f[i] + f[j]*cnt[j][i]%MOD)%MOD
print(f[n-1])

动态规划DP + 约数优化

# http://118.190.20.162/view.page?gpid=T125
from collections import defaultdict
n = int(input())
a = list(map(int,input().split()))
N = 1010
MOD = int(1e9+7)
'''
设f[i]为前i个障碍物所能生成的最多可能性
设 cnt[i][j]为从第i个障碍物到第j个障碍物的可行方案数
想要求cnt[i][j]只需从位置i枚举所有间隔的可能性,看是否能在不触碰到i,j之间的障碍物的情况下到达
'''
f = [0]*N
f[0] = 1
q = defaultdict(list)
M = 100010
for i in range(1,M):
    for j in range(2*i,M,i):
        q[j].append(i)
# 动态规划
for i in range(1,n): # 每轮添加一个障碍物,计算添加后的方案总数
    st = defaultdict(int) # 清空状态数组
    for j in range(i-1,-1,-1):
        d = a[i] - a[j]
        cnt = 0
        for k in q[d]: # 枚举d的所有的因子,也就是所有可能的方案
            if not st[k]: # 如果此因子之前没使用过,方案数加一,并标记当前因子已被用过
                cnt += 1
                st[k] = 1
        # 手动添加d本身,因为下一轮如果按照这个间隔植树就会撞上本轮添加的障碍物
        st[d] = 1 # 因为最后一个障碍物必选
        f[i] = (f[i] + f[j]*cnt)%MOD
print(f[n-1])


相关文章
|
1月前
|
机器学习/深度学习 数据采集 TensorFlow
使用Python实现智能食品加工优化的深度学习模型
使用Python实现智能食品加工优化的深度学习模型
166 59
|
27天前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
25天前
|
缓存 大数据 C语言
python优化
python优化
38 5
|
1月前
|
机器学习/深度学习 数据采集 运维
使用 Python 实现深度学习模型:智能食品生产线优化
使用 Python 实现深度学习模型:智能食品生产线优化
55 13
|
1月前
|
机器学习/深度学习 自然语言处理 语音技术
Python在深度学习领域的应用,重点讲解了神经网络的基础概念、基本结构、训练过程及优化技巧
本文介绍了Python在深度学习领域的应用,重点讲解了神经网络的基础概念、基本结构、训练过程及优化技巧,并通过TensorFlow和PyTorch等库展示了实现神经网络的具体示例,涵盖图像识别、语音识别等多个应用场景。
52 8
|
1月前
|
机器学习/深度学习 数据采集 供应链
使用Python实现智能食品供应链优化的深度学习模型
使用Python实现智能食品供应链优化的深度学习模型
44 8
|
1月前
|
机器学习/深度学习 算法 数据可视化
使用Python实现深度学习模型:智能食品配送优化
使用Python实现深度学习模型:智能食品配送优化
72 2
|
1月前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
53 2
|
1月前
|
搜索推荐 Python
快速排序的 Python 实践:从原理到优化,打造你的排序利器!
本文介绍了 Python 中的快速排序算法,从基本原理、实现代码到优化方法进行了详细探讨。快速排序采用分治策略,通过选择基准元素将数组分为两部分,递归排序。文章还对比了快速排序与冒泡排序的性能,展示了优化前后快速排序的差异。通过这些分析,帮助读者理解快速排序的优势及优化的重要性,从而在实际应用中选择合适的排序算法和优化策略,提升程序性能。
43 1
|
1月前
|
运维 监控 Linux
自动化运维:如何利用Python脚本优化日常任务##
【10月更文挑战第29天】在现代IT运维中,自动化已成为提升效率、减少人为错误的关键技术。本文将介绍如何通过Python脚本来简化和自动化日常的运维任务,从而让运维人员能够专注于更高层次的工作。从备份管理到系统监控,再到日志分析,我们将一步步展示如何编写实用的Python脚本来处理这些任务。 ##