k倍多重正整数集

k倍多重正整数集
牛客网:k倍多重正整数集

题目描述

k倍多重正整数集合的定义是:在一个多重集合(元素可以重复)中,不存在一个正整数是另一个正整数的k倍。 现在小M有n个正整数,你可以选择其中一些数构成k倍多重正整数集合。请求出最多能选出多少数来构成它。

输入描述:
第一行有两个整数n, k(1 <= n <= 10^5, 1 <= k <= 10^9)。

接下来一行有n个正整数a1, a2, …, an (1 <= ai <= 10^9)。

输出描述:
最多能选出多少数构成k倍多重正整数集合。

示例1

输入
6 2
2 3 6 5 4 10

输出
3

思路:
首先声明,这个思路用python超时了,但是一定是正确的思路。
我们将连续的符合k倍多重正整数归为同类,比如k=2时,【2,4,8,16】就是一类,但是如果没有32,那么64和上面的数组元素不属于同一类。因此,即便有k倍的关系,必须要连续才可以。

因此我们先用哈希表存储数据和对应的次数,然后将哈希表的键值排序(排序的目的是为了保证后面不会出现比前面小的元素。比如【4,8,16】已经整理好,但是后面又出现了2,那么2是可以和他们归为一类,但是出现的顺序不对,那么就会出现自己单独归为一类,出现错误),用一个while循环将同一类的元素归为一类,record记录出现过的元素,并用temp记录相应键值的次数。

temp这个数组很有用,它记录的是同一类连续元素出现的次数。那么对于这个数组,我用了动态规划计算其最大的次数。比如dp=【3,1,3,4】,我们取max(dp【-1】,dp【-2】)。dp的舒适化很重要,当dp的长度小于3时,取其中的最大值即可。dp长度大于等于3时,dp【0】= num【0】,dp【1】=max(num【0】,num【1】),dp【2】=num【0】+num【2】,之后的dp【i】= max(dp【i-2】,dp【i-3】)+num【i】。

对每一类的temp,我们用select_num 计算最大的出现次数即可。简而言之,select_num实现了如下功能:计算出不连续子数组最大和。用res累加所有类的次数就是答案。python通过了95%的例子,最后那个例子一定是一万多以上的数字,不然的话不会如此耗时。而且,牛客网中python2版本运行速度往往优于python3。

def select_num(num):
    tem = [num[i] for i in range(len(num))]
    if len(tem)<=2:
        return max(tem)
    else:
        dp = [0 for _ in range(len(tem))]
        dp[0] = tem[0]
        dp[1] = max(tem[0],tem[1])
        dp[2] = dp[0]+tem[2]
        for j in range(3,len(tem)):
            dp[j] = max(dp[j-2],dp[j-3])+tem[j]
    return max(dp[-1],dp[-2])
#数据的读取
n, k = list(map(int,input().split()))
num = list(map(int,input().split()))
if n==1:
    print(1) # 只有一个数时,直接输出1
elif k==1:
    print(len(set(num))) #k=1时,输出集合元素个数就行
else:
    dirc = {}
    for i in num: # 用哈希表存储元素及其相应出现的次数
        dirc[i] = dirc.get(i,0)+1
    record = [] #记录出现过的元素
    ans = 0
    new = sorted(dirc) #对哈希表的键值排序,保证循环时大的数只在后面出现
    for i in new:
        if i not in record: # 若已经在记录中出现过,那么就属于已经在temp中统计过的,不再重复统计。
            temp = [dirc[i]] #不在记录中,那就用temp记录相应的次数。
            number = i*k
            record.append(i)
            while number in dirc and number not in record: # 若i及其同类在字典中出现但不在temp中
                temp.append( dirc[number])
                record.append(number)
                number = number * k
            ans += select_num(temp)
    print(ans)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值