蓝桥 包子凑数【第八届】【省赛】【A组】python gcd 完全背包

想说的话

大家好🌼🌼,我是 @愿此后再无WA,可以叫我小A,也可以叫我愿愿💡💡,一位阳光帅小伙,对算法领域比较感兴趣。如果我的文章对您有用,欢迎持续关注,我们一起进步!🎈🎈

很抱歉各位😪😪,现离蓝桥杯比赛不到一个月时间,我临时改变了计划,转为全心备战蓝桥,因为这个省一对我来说太重要了,也是我最后一次机会,我一定要拿到手📌📌,那么这样的话我在博客上花的时间就会少了很多,也将导致博客文章质量明显下降,在此我给大家说声抱歉💥💥


🌟🌟这些日子我真的很开心,博客上能遇到一群志同道合的兄弟姐妹真的很幸福,没有你们的支持与鼓励我早就坚持不下去了,因为有你们我才能走的更远☀️☀️熬过这段时期我一定会回来的,爱你们❤️❤️

包子凑数

在这里插入图片描述
在这里插入图片描述

分析

先理一下题目,题目的意思就是说,给你一串数字,找出所有不能通过这些数字拼出来数字的数量。比如,给出数字3,5。不能拼出来的数字有1,2,4,6,7…。可能有人以为无论给出哪些数字,不能拼出来的数字都是无限个,所以没法下手,其实呢,数学中已经得出了一个推论。
在这里插入图片描述
这个推论很重要,如果两个数互质,就有无法组成的最大整数,那么不能凑出的数字也就一一确定。还是以刚才3,5为例,根据上面的式子可以得出无法组成的最大整数是7(35-3-5=7)。8可以通过5+3得出,9可以通过33得出,10可以通过5+5得出。当连续凑出min(a,b)个数之后,之后的数字必定能凑出,11可以通过8+3得出,12可以通过9+3得出…等等。因此,3与5不能凑出数字的数量也就确定了。而不互质呢?如果两个数不互质,那么一定没有无法组成的最大整数,也就没有确定的无法组成数字的数量。为什么呢?因为两个数不互质,那么这两个数都是他们最大公约数的倍数,相当于那个公约数乘上若干数字得到这两个数的,而这样的话本体也就一个数,就是那个公约数。

思路

我们已经知道了唯有两数互质才有无法组成的最大整数,当出现N个数时,同样这么判断:遍历存放数字的列表,使用gcd两两比较得出最大公约数,如果是1(互质),就说明这个组里面一定存在无法组成的最大数。如果不是1,用这个公约数与下一个数字比较,如果是1,那么这个数字一定至少与前面的一个数字互质,如果不是1,继续用他们的最大公约数与下一个数比较,重复上述操作,直到找到两数互质或遍历完列表,如果遍历完列表gcd的结果仍不是1说明这些数之间没有一个数是互质的,可以返回“INF”,反之,如果发现至少有一个数互质,那么就能得到无法组成的最大数,然后在这个区间寻找无法凑出数字的数量。

找数的话就是用完全背包的思想去寻找,也就是一个数字可以取任意多次。如果不清楚这个知识点可以上bilibili大学自行学习。做这题的时候我才发现并理解原来一维01背包是从后往前遍历,一维完全背包是从前往后遍历的。

代码

我们可以根据 “当连续凑出min(a,b,c,d…)个数之后,之后的数字必定能凑出” 这一条件提前返回结果

def gcd(a,b):
    
    if a > b:
        a, b = b, a

    while a > 0:
        a, b = b % a, a

    return b
    

def func():
    N = int(input())

    if N == 1:
        return "INF"

    nums = []
    for i in range(N):
        nums.append(int(input()))

    nums.sort()
    
    r = gcd(nums[0],nums[1])

    for i in range(2,N+1):
        if i < N:
            r = gcd(r,nums[i])
            if r == 1:  # 当找到两数互质的条件时可以马上结束
                break
    if r != 1:
        return "INF"
	
	 # 这里1e4足够了,因为100以内最大两个互质的数就是99跟100,他们乘积必定小于1e4
    dp = [False for i in range(int(1e4+1))]
    ans = 0
    cnt = 0
    mi = nums[0]  # 因为已经排序了,最小的数必定是第一个
    for i in range(1, int(1e4+1)):
        for j in nums:
            if j > i:
                continue
            if j == i or dp[i - j]: # 凑到数的情况
                cnt += 1
                dp[i] = True
                break  

        if not dp[i]:  # 如果没凑到总数量+1,以及连续数要清零
            ans += 1
            cnt = 0

		# 如果连续数已经等于数组最小值了,可以立马返回结果,因为后面的数必定能抽出来
        if cnt == mi:  
            return ans
print(func())

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愿此后再无WA

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

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

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

打赏作者

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

抵扣说明:

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

余额充值