【编程练习】选择物品

题目来源:牛客,阿里巴巴编程题(2星),第2题

题目描述

在这里插入图片描述

题解

法一:很绕的想法
自认为本质是实现n进制的加法,但觉得很难实现,便借助了进制转换。
思想:以n=8,m=5为例。最小的输出(minn)肯定是“1 2 3 4 5”,最大的输出(maxn)是“4 5 6 7 8”。为了计数方便,借助了映射转换:
minn其实可以映射为“0 0 0 0 0”,而maxn可以映射为“3 3 3 3 3”,映射到4进制空间。
所以,输出其实可以看做5位(m位)4进制(n-m+1进制)数,即“a0 a1 a2 a3 a4”。
接下来要做的就是:
非十进制的递增遍历(加法),和,输出映射。
1)为了实现n-m+1进制的递增遍历,我借助了十进制。十进制的递增遍历很简单,+1即可,所以,我先将n-m+1进制的maxn转换成十进制,以此作为上限。而后,十进制每进行一次+1,就进行一次进制转换,得到对应的n-m+1进制数。
2)映射,通过nums实现。nums是m*(n-m+1)维,第一层包含m个list,即m位数,每位数(每个list)都有n-m+1个待选项,对应于n-m+1进制中的0~n-m。
以n=8,m=5为例,nums=[[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7],[5,6,7,8]]。
具体代码如下:

def trans(x): #进制转换
    global base,m
    ans,tmpy = [],base
    while x!=0:
        y = x%base
        # 注意,这里需要筛除0010这类数,以保证位数递增
        if tmpy<y:
            return []
        else:
            tmpy = y
        x = x // base
        ans.insert(0,y)
    ans = [0]*(m-len(ans))+ans
    return ans
def main():
    global trans,base,m
    n,m = map(int,input().split())
    if n==m: # 特殊情况,这时无需进行进制转换
        print(' '.join('%s' %id for id in range(1,m+1)))
        return
    # 以下三行用于构造nums
    nums = [[i for i in range(1,n-m+2)]]
    for i in range(m-1):
        nums.append([j+1 for j in nums[-1]])
    # 进制数
    base = n-m+1
    # base进制下的maxn转换为十进制下的maxn
    maxn = int(str(n-m)*m,base)
    # 开始遍历
    for i in range(maxn+1):
        ind = trans(i)
        if len(ind)>0:
            obj = [nums[j][ind[j]] for j in range(m)]
            print(' '.join('%s' %id for id in obj))
    return

if __name__=='__main__':
    main()

方法是蠢了些,就这还花了1h才写出来,很难不emo。。。

法二:递归
就很像地图填色,递归的参数分别是:num为当前要填充的数字的最小值;obj为目标数组。
当obj长度为m时,就print,并return【无意间发现,这里不return也能过】
当obj还没填满时,开始逐一添加元素,每次添加的元素的起始值都比上一位大1【保证递增】
注意,这里统一限制填充的元素的最大值为n,这样做并不会出错,例如,虽然第一位数字的最大值应该是n-m+1,但由于该位还没到填充n-m+2时obj的长度就已经到m了,所以 for循环执行不到n-m+2。

def recursion(num,obj):
    if len(obj)==m:
        print(' '.join('%s'%id for id in obj))
    for i in range(num,n+1):
        obj.append(i)
        recursion(i+1, obj)
        obj.pop() # 和地图填色一样,当该值填充过后,需要pop,进行下一个
    return

def main():
    global n,m
    n,m = map(int,input().split())
    if m == n:
        print(' '.join('%s'%id for id in range(1,m+1)))
        return
    recursion(1,[])
    return

if __name__=='__main__':
    main()

小总结

虽然法一很绕,但还是需要总结一下很多遗忘的知识:

  • list转str:''.join(list),但需要注意的是,如果list中的元素是int型,则需要用''.join('%s'%id for id in list)
  • str转list:list(str),但这种方法需要import string;某些情况下,用split也行
  • 进制转换:
    • int(str,base)可以将base进制的str转成十进制数
    • bin(intNum) // 返回10进制的intNum 的2进制表示字符串
    • oct(intNum) // 返回10进制的intNum 的8进制表示字符串
    • hex(intNum) // 返回10进制的intNum 的16进制表示字符串
    • ord(character) // 返回character 所对应的字符在unicode编码的顺序
  • 十进制转n进制:
def trans(n,base):
    #n为待转换的十进制数,base为进制,取值为2-16
    ## 由于十进制以上需要用到字母,所以用maplist做一个映射
    maplist=[0,1,2,3,4,5,6,7,8,9,'A','b','C','D','E','F']
    ans=[]
    if n==0:
    	return 0
    while n!=0:
    	y=n%x  # 余数
        n=n//x  # 商
        ans.insert(0,y) # 注意这里需要逆序插入
    ans = [maplist[i] for i in ans]
    return ''.join('%s'%id for id in ans)

每日一emo

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值