【蓝桥杯-砝码称重】

蓝桥杯-砝码称重

问题描述:
你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · WN
请你计算一共可以称出多少种不同的重量?
注意砝码可以放在天平两边

输入的第一行包含一个整数 N
第二行包含 N 个整数:W1, W2, W3, · · · WN输出一个整数代表答案
输出一个整数代表答案

样例输入
3
1 4 6
样例输出
10

这篇文章主要是分析动态规划解决此类问题。

不用动态规划,这样的问题也可以解决,比如用枚举法


##枚举法,时间超时
while True:
    try:
        N = int(input())
        W = list(map(int,input().split()))
        nums = W.copy()
        dp = [[0]]
        for i in range(N):
            dp_list = []
            for j in range(len(dp[i])):
                temp_dp1 = dp[i][j]+W[i]*1
                temp_dp2 = abs(dp[i][j]+W[i]*(-1))
                temp_dp3 = dp[i][j]+W[i]*0
                if temp_dp1 not in nums:
                    nums.append(temp_dp1)
                if temp_dp2 not in nums:
                    nums.append(temp_dp2)
                if temp_dp3 not in nums:
                    nums.append(temp_dp3)
                dp_list.extend([temp_dp1,temp_dp2,temp_dp3])
            dp.append(dp_list)
        print(len(nums)-1)
    except:
        break

但这个时间是超时的,因为枚举法消耗资源过多,算法复杂度随着N的增大,呈现指数增长,其中,我列举了3中状态,对于每个砝码他可以选择放在左边、右边、或者不放。

接下来我们来看动态规划解法

N = int(input())
weights = list(map(int,input().split()))
maxlen = sum(weights) #这是天平所能称重的最大范围
dp = [[0]*(maxlen*2+1) for i in range(N+1)]  #多生成一列和一行
for i in range(N+1):
    dp[i][0] = 1
for i in range(1,N+1):
    for j in range(maxlen+1):    #首先初始化最边际地带
        if dp[i-1][j] == 1: #如果前i-1能取到j
            dp[i][j] = 1 ##前i-1个能取到,前i个一定能取到
            dp[i][j+weights[i-1]] = 1
            dp[i][abs(j-weights[i-1])] = 1
print(sum(dp[N])-1) #多一个0

动态规划能解决的问题,有这样的特征,有重复计算值、上一子问题可以作为当前子问题的提取库。

N = int(input())
weights = list(map(int,input().split()))
maxlen = sum(weights) #这是天平所能称重的最大范围
dp = [[0]*(maxlen*2+1) for i in range(N+1)]

一般dp是要定义一个多一行一列的取值库,不过对此问题而言,需要变动一下成二倍列,多一行的取值库,为了防止超出索引。

for i in range(1,N+1):
    for j in range(maxlen+1):    #首先初始化最边际地带
        if dp[i-1][j] == 1: #如果前i-1能取到j
            dp[i][j] = 1 ##前i-1个能取到,前i个一定能取到
            dp[i][j+weights[i-1]] = 1
            dp[i][abs(j-weights[i-1])] = 1
print(sum(dp[N])-1) #多一个

动态规划主要切入点是分析dp[i]与dp[i-1]的关系,有啥关心呢?

  • dp[i][j]代表的是前i+1项能否取到j值,能则为1否者为0,那么dp[i-1][j]为1,dp[i][j]一定为1,这样可以得到关系dp[i-1][j]=dp[i][j],条件前者为1
    根据题意,前面提到-对于每个砝码他可以选择放在左边、右边、或者不放,这咋体现呢?
    放右边边:dp[i][j+weights[i-1]],一开始小白会???????,这怎么回事呢?j+weigths不会超额吗?答案是不会,因为前面条件的限制,dp[x][j=maxlen]会在最后出现。
    放左边:dp[i][abs(j-weigths[i-1])],涉及到减号,加上abs
    不放:过程中已经体现了,不信你频频
    动态规划核心是前i!前i!前i!,啥题目都这样。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 现在有m个砝码,第i个砝码的质量为wi,每个砝码的个数不限。要将这些砝码放到天平上进行称重,天平两边的砝码质量必须相等,求可以称出的不同的质量数。 输入格式 第一行包含一个整数m,表示砝码的个数。 第二行包含m个整数,分别表示每个砝码的质量。 输出格式 输出一个整数,表示可以称出的不同的质量数。 数据范围 1≤m≤1000, 1≤wi≤1000 样例 输入样例: 3 1 2 3 输出样例: 7 算法 (01背包) 这道题目可以用01背包来解决,把所有的砝码都放在背包里,那么就相当于是用砝码的质量去凑数,只要能够凑出的数与总量是一致的,那么就说明是一种情况,最后统计出来即可。 时间复杂度 由于使用了01背包,所以时间复杂度为O(m * sum)。 C++ 代码 思路: 将之前的四维DP数组优化成了一维数组,变成了从后往前遍历。 背包问题的本质,就是对于一个包,往里面装物品,最终使得包里的价值达到某个值,所以我们只需要从后往前遍历,对于每一个物品,都通过改变当前包的容量,来达到包里的价值增加的目的,直到遍历完所有的物品,那么包里的价值也就达到了最大的值。 这是一个典型的背包问题,可以使用01背包,完全背包,多重背包来解决。 这道题目使用01背包,因为砝码的数量不限制,所以每一个砝码都可以被选择多次。 C++ 代码

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值