P1083 [NOIP2012 提高组] 借教室 Python(二分答案+ 差分数组)

        本题我没有AC,最高得分55。翻看了Python提交记录发现没有一个能够AC,最高分为70,我认为应该是洛谷的问题,Python本身相比较C++来说就比较慢,翻遍全网也没有找到此题的Python详细的代码,故学习洛谷C++大佬的思路,编写Python代码,写下此篇记录一下学习过程,加之抛砖引玉,希望有大佬给出分数相对较高的代码。

题目链接:https://www.luogu.com.cn/problem/P1083

本题有两种算法:暴力法 和 二分答案 + 差分数组


1.暴力法

        遍历订单天数,维护一个数组,用于添加天数,每次循环一个订单累加需求并与原有教室数量进行比较,若大于则停止循环记录订单编号输出结果,此法最高40分。

# 输入数据
n, m = map(int, input().split())
rooms = list(map(int, input().split()))
classroom = [0] * n
bill = []
flag = 0
arr = [0] * (n + 1)
for i in range(m):
    bill.append(list(map(int, input().split())))

for i in range(m):  # 循环订单
    for j in range(bill[i][1], bill[i][2] + 1):
        classroom[j - 1] += bill[i][0]
    for a in range(n):
        if classroom[a] > rooms[a]:
            flag = i + 1  # 记录订单编号
            break
    if flag != 0:
        break
if flag == 0:
    print(0)
else:
    print(-1)
    print(flag)

2.二分+差分

        对于其提供的订单结果来看,“我们要按照订单的先后顺序依次为每份订单分配教室”,由此句我们可以得知当中间的一份订单无法满足时,后续的所有订单也不能够满足,我们可以使用二分答案,对所给的订单进行二分取舍。

        后面的问题是二分后如何判断该订单是否能够满足?因为每个订单后面都规定了相应的范围,可以用差分来简便计算。

        先简单介绍一下什么是差分数组,差分就是前缀和的逆序,先举个例子:

        设有数组                                        arr = [1, 3, 7, 5, 2]

        我们可以得出每个元素之间的差    d = [1, 2, 4, -2, -3]

        当我们对d前n项求和是可以发现  sum = [1, 3, 7, 5, 2],sum居然 == arr,这就是其性质。

        对此我们有相应的公式:    [L, R] + v <=> d[L] + v, d[R + 1] - v

假设我们要对arr中[2, 4]范围中的数+2,对[1, 3]范围中的数-1,利用上述公式我们可以得到

        [1, 3] - 1 <=> d[1] - 1, d[3] + 1                [2, 4] + 2 <=> d[2] + 2,d[4+1]-2

此时d更新为            d = [1, 1, 6, -1, -3](对于越界的数可以不用管,也可以后续append(0) )        

此时再将数组d前n项求和发现与你算的结果相同:  ans = [1, 2, 8, 6, 4]

        那我们为什么要在(R+1)后面再减去这个数呢?假设d = [0, 0, 0, 0, 0],若对[1, 3]范围-1会得到d = [0, -1, 0, 0, 0],因为我们最后要进行累加,进行前缀和会变成 d = [0, -1, -1, -1, -1],此时我们发现位置4也进行了一次-1操作,所以我们要在(R+1)的位置进行减去操作。

        下面上代码:

# 输入数据
n, m = map(int, input().split())
rooms = list(map(int, input().split()))
classroom = [0] * (n + 1)  # 设立n+1是为了防止越界,因为存在前文所说的(R+1)项
bill = []
arr = [0] * (n + 1)  # 设立n+1是为了后续判别方便第一项arr[i - 1](i = 1)累加
for i in range(m):
    bill.append(list(map(int, input().split())))

# 进行判断
def diff(x):  # 判断第x号订单是否符合条件
    classroom = [0] * (n + 1)  # 差分数组全置0
    for i in range(x):  # 补充差分数组(注意数组越界问题)
        classroom[bill[i][1] - 1] += bill[i][0]  # 套公式对左边界进行 + 1 
        classroom[bill[i][2]] -= bill[i][0]  # 套公式对右边界进行 - 1
    for i in range(1, n + 1):  # 开始判断(注意数组越界问题)
        arr[i] = arr[i - 1] + classroom[i - 1]  # 进行前缀和
        if arr[i] > rooms[i - 1]:  # 请求数量>所有,订单失效
            return 0
    return 1


left = 0
right = m
if diff(right):  # 先判断最后一个订单是否能够完成
    print(0)
else:
    while left <= right:  # 对订单进行二分
        mid = (left + right) // 2
        if diff(mid):  # 此订单可以,代表前面所有订单都可以执行,向后二分
            left = mid + 1
        else:  # 此订单不可,代表后面的订单全部失效,向前二分
            right = mid - 1
    print(-1)
    print(left)

​

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值